diff --git a/depends/packages/crate_sapling_crypto.mk b/depends/packages/crate_sapling_crypto.mk index 376fca9a3..2a721ab27 100644 --- a/depends/packages/crate_sapling_crypto.mk +++ b/depends/packages/crate_sapling_crypto.mk @@ -3,8 +3,8 @@ $(package)_crate_name=sapling-crypto $(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=5eb4040bc223a689341b3f1a1fc53d6064c4c032b23ae0c2c653b063e1da24db -$(package)_git_commit=e554b473dd10885d232f42237c13282f5b6fee43 +$(package)_sha256_hash=5062b9e752066ad959f14063d496b0a156ce96004a13a6823494249793c01f96 +$(package)_git_commit=7beeb52730e24724ee10ea2458ecf7776cb59c58 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index b08cd2c83..96f3143d1 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -3,8 +3,8 @@ $(package)_version=0.1 $(package)_download_path=https://github.com/zcash/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=b63ba98d569d332764f27706038c04d03ac7e2c836dc15dc4eaa24b04b8c7f4a -$(package)_git_commit=6cc1813ae3bb1e42224fd8ca0a8977b95c576738 +$(package)_sha256_hash=c5442a57d8961aab12fd395a5004edbb96b973511fab3949a087faa2a865a002 +$(package)_git_commit=ef676eff5084d394e6c6eaf2b9d9817effe662a7 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config diff --git a/depends/patches/librustzcash/cargo.config b/depends/patches/librustzcash/cargo.config index c0229a9cc..23e6b808a 100644 --- a/depends/patches/librustzcash/cargo.config +++ b/depends/patches/librustzcash/cargo.config @@ -8,7 +8,7 @@ replace-with = "vendored-sources" [source."https://github.com/zcash-hackworks/sapling-crypto"] git = "https://github.com/zcash-hackworks/sapling-crypto" -rev = "e554b473dd10885d232f42237c13282f5b6fee43" +rev = "7beeb52730e24724ee10ea2458ecf7776cb59c58" replace-with = "vendored-sources" [source.vendored-sources] diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 0b80921a9..600ae9301 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -32,6 +32,12 @@ JSON_TEST_FILES = \ test/data/merkle_witness_serialization.json \ test/data/merkle_path.json \ test/data/merkle_commitments.json \ + test/data/merkle_roots_sapling.json \ + test/data/merkle_roots_empty_sapling.json \ + test/data/merkle_serialization_sapling.json \ + test/data/merkle_witness_serialization_sapling.json \ + test/data/merkle_path_sapling.json \ + test/data/merkle_commitments_sapling.json \ test/data/g1_compressed.json \ test/data/g2_compressed.json diff --git a/src/chain.h b/src/chain.h index 906267326..b5a1a3ba6 100644 --- a/src/chain.h +++ b/src/chain.h @@ -152,10 +152,10 @@ public: boost::optional nCachedBranchId; //! The anchor for the tree state up to the start of this block - uint256 hashAnchor; + uint256 hashSproutAnchor; //! (memory only) The anchor for the tree state up to the end of this block - uint256 hashAnchorEnd; + uint256 hashFinalSproutRoot; //! Change in value held by the Sprout circuit over this block. //! Will be boost::none for older blocks on old nodes until a reindex has taken place. @@ -169,7 +169,7 @@ public: //! block header int nVersion; uint256 hashMerkleRoot; - uint256 hashReserved; + uint256 hashFinalSaplingRoot; unsigned int nTime; unsigned int nBits; uint256 nNonce; @@ -192,15 +192,15 @@ public: nChainTx = 0; nStatus = 0; nCachedBranchId = boost::none; - hashAnchor = uint256(); - hashAnchorEnd = uint256(); + hashSproutAnchor = uint256(); + hashFinalSproutRoot = uint256(); nSequenceId = 0; nSproutValue = boost::none; nChainSproutValue = boost::none; nVersion = 0; hashMerkleRoot = uint256(); - hashReserved = uint256(); + hashFinalSaplingRoot = uint256(); nTime = 0; nBits = 0; nNonce = uint256(); @@ -218,7 +218,7 @@ public: nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; - hashReserved = block.hashReserved; + hashFinalSaplingRoot = block.hashFinalSaplingRoot; nTime = block.nTime; nBits = block.nBits; nNonce = block.nNonce; @@ -250,7 +250,7 @@ public: if (pprev) block.hashPrevBlock = pprev->GetBlockHash(); block.hashMerkleRoot = hashMerkleRoot; - block.hashReserved = hashReserved; + block.hashFinalSaplingRoot = hashFinalSaplingRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; @@ -366,13 +366,13 @@ public: READWRITE(branchId); } } - READWRITE(hashAnchor); + READWRITE(hashSproutAnchor); // block header READWRITE(this->nVersion); READWRITE(hashPrev); READWRITE(hashMerkleRoot); - READWRITE(hashReserved); + READWRITE(hashFinalSaplingRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); @@ -391,7 +391,7 @@ public: block.nVersion = nVersion; block.hashPrevBlock = hashPrev; block.hashMerkleRoot = hashMerkleRoot; - block.hashReserved = hashReserved; + block.hashFinalSaplingRoot = hashFinalSaplingRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; diff --git a/src/coins.cpp b/src/coins.cpp index 0559a8820..0ae74af1d 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -42,16 +42,19 @@ bool CCoins::Spend(uint32_t nPos) Cleanup(); return true; } -bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; } -bool CCoinsView::GetNullifier(const uint256 &nullifier, NullifierType type) const { return false; } +bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; } +bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { return false; } +bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; } bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } uint256 CCoinsView::GetBestBlock() const { return uint256(); } -uint256 CCoinsView::GetBestAnchor() const { return uint256(); }; +uint256 CCoinsView::GetBestAnchor(ShieldedType type) const { return uint256(); }; bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } @@ -59,19 +62,22 @@ bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } -bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); } -bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, NullifierType type) const { return base->GetNullifier(nullifier, type); } +bool CCoinsViewBacked::GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetSproutAnchorAt(rt, tree); } +bool CCoinsViewBacked::GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { return base->GetSaplingAnchorAt(rt, tree); } +bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return base->GetNullifier(nullifier, type); } bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } -uint256 CCoinsViewBacked::GetBestAnchor() const { return base->GetBestAnchor(); } +uint256 CCoinsViewBacked::GetBestAnchor(ShieldedType type) const { return base->GetBestAnchor(type); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, - CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapSproutNullifiers, mapSaplingNullifiers); } + CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} @@ -85,7 +91,8 @@ CCoinsViewCache::~CCoinsViewCache() size_t CCoinsViewCache::DynamicMemoryUsage() const { return memusage::DynamicUsage(cacheCoins) + - memusage::DynamicUsage(cacheAnchors) + + memusage::DynamicUsage(cacheSproutAnchors) + + memusage::DynamicUsage(cacheSaplingAnchors) + memusage::DynamicUsage(cacheSproutNullifiers) + memusage::DynamicUsage(cacheSaplingNullifiers) + cachedCoinsUsage; @@ -110,9 +117,9 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const } -bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { - CAnchorsMap::const_iterator it = cacheAnchors.find(rt); - if (it != cacheAnchors.end()) { +bool CCoinsViewCache::GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + CAnchorsSproutMap::const_iterator it = cacheSproutAnchors.find(rt); + if (it != cacheSproutAnchors.end()) { if (it->second.entered) { tree = it->second.tree; return true; @@ -121,11 +128,11 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr } } - if (!base->GetAnchorAt(rt, tree)) { + if (!base->GetSproutAnchorAt(rt, tree)) { return false; } - CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first; + CAnchorsSproutMap::iterator ret = cacheSproutAnchors.insert(std::make_pair(rt, CAnchorsSproutCacheEntry())).first; ret->second.entered = true; ret->second.tree = tree; cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); @@ -133,17 +140,40 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr return true; } -bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, NullifierType type) const { +bool CCoinsViewCache::GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { + CAnchorsSaplingMap::const_iterator it = cacheSaplingAnchors.find(rt); + if (it != cacheSaplingAnchors.end()) { + if (it->second.entered) { + tree = it->second.tree; + return true; + } else { + return false; + } + } + + if (!base->GetSaplingAnchorAt(rt, tree)) { + return false; + } + + CAnchorsSaplingMap::iterator ret = cacheSaplingAnchors.insert(std::make_pair(rt, CAnchorsSaplingCacheEntry())).first; + ret->second.entered = true; + ret->second.tree = tree; + cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); + + return true; +} + +bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, ShieldedType type) const { CNullifiersMap* cacheToUse; switch (type) { - case SPROUT_NULLIFIER: + case SPROUT: cacheToUse = &cacheSproutNullifiers; break; - case SAPLING_NULLIFIER: + case SAPLING: cacheToUse = &cacheSaplingNullifiers; break; default: - throw std::runtime_error("Unknown nullifier type"); + throw std::runtime_error("Unknown shielded type"); } CNullifiersMap::iterator it = cacheToUse->find(nullifier); if (it != cacheToUse->end()) @@ -158,10 +188,17 @@ bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, NullifierType type) return tmp; } -void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) { +template +void CCoinsViewCache::AbstractPushAnchor( + const Tree &tree, + ShieldedType type, + Cache &cacheAnchors, + uint256 &hash +) +{ uint256 newrt = tree.root(); - auto currentRoot = GetBestAnchor(); + auto currentRoot = GetBestAnchor(type); // We don't want to overwrite an anchor we already have. // This occurs when a block doesn't modify mapAnchors at all, @@ -169,24 +206,67 @@ void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) { // different way (make all blocks modify mapAnchors somehow) // but this is simpler to reason about. if (currentRoot != newrt) { - auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry())); - CAnchorsMap::iterator ret = insertRet.first; + auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CacheEntry())); + CacheIterator ret = insertRet.first; ret->second.entered = true; ret->second.tree = tree; - ret->second.flags = CAnchorsCacheEntry::DIRTY; + ret->second.flags = CacheEntry::DIRTY; if (insertRet.second) { // An insert took place cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); } - hashAnchor = newrt; + hash = newrt; } } -void CCoinsViewCache::PopAnchor(const uint256 &newrt) { - auto currentRoot = GetBestAnchor(); +void CCoinsViewCache::PushSproutAnchor(const ZCIncrementalMerkleTree &tree) { + AbstractPushAnchor( + tree, + SPROUT, + cacheSproutAnchors, + hashSproutAnchor + ); +} + +void CCoinsViewCache::PushSaplingAnchor(const ZCSaplingIncrementalMerkleTree &tree) { + AbstractPushAnchor( + tree, + SAPLING, + cacheSaplingAnchors, + hashSaplingAnchor + ); +} + +template<> +void CCoinsViewCache::BringBestAnchorIntoCache( + const uint256 ¤tRoot, + ZCIncrementalMerkleTree &tree +) +{ + assert(GetSproutAnchorAt(currentRoot, tree)); +} + +template<> +void CCoinsViewCache::BringBestAnchorIntoCache( + const uint256 ¤tRoot, + ZCSaplingIncrementalMerkleTree &tree +) +{ + assert(GetSaplingAnchorAt(currentRoot, tree)); +} + +template +void CCoinsViewCache::AbstractPopAnchor( + const uint256 &newrt, + ShieldedType type, + Cache &cacheAnchors, + uint256 &hash +) +{ + auto currentRoot = GetBestAnchor(type); // Blocks might not change the commitment tree, in which // case restoring the "old" anchor during a reorg must @@ -195,18 +275,41 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) { // Bring the current best anchor into our local cache // so that its tree exists in memory. { - ZCIncrementalMerkleTree tree; - assert(GetAnchorAt(currentRoot, tree)); + Tree tree; + BringBestAnchorIntoCache(currentRoot, tree); } // Mark the anchor as unentered, removing it from view cacheAnchors[currentRoot].entered = false; // Mark the cache entry as dirty so it's propagated - cacheAnchors[currentRoot].flags = CAnchorsCacheEntry::DIRTY; + cacheAnchors[currentRoot].flags = CacheEntry::DIRTY; // Mark the new root as the best anchor - hashAnchor = newrt; + hash = newrt; + } +} + +void CCoinsViewCache::PopAnchor(const uint256 &newrt, ShieldedType type) { + switch (type) { + case SPROUT: + AbstractPopAnchor( + newrt, + SPROUT, + cacheSproutAnchors, + hashSproutAnchor + ); + break; + case SAPLING: + AbstractPopAnchor( + newrt, + SAPLING, + cacheSaplingAnchors, + hashSaplingAnchor + ); + break; + default: + throw std::runtime_error("Unknown shielded type"); } } @@ -280,10 +383,21 @@ uint256 CCoinsViewCache::GetBestBlock() const { } -uint256 CCoinsViewCache::GetBestAnchor() const { - if (hashAnchor.IsNull()) - hashAnchor = base->GetBestAnchor(); - return hashAnchor; +uint256 CCoinsViewCache::GetBestAnchor(ShieldedType type) const { + switch (type) { + case SPROUT: + if (hashSproutAnchor.IsNull()) + hashSproutAnchor = base->GetBestAnchor(type); + return hashSproutAnchor; + break; + case SAPLING: + if (hashSaplingAnchor.IsNull()) + hashSaplingAnchor = base->GetBestAnchor(type); + return hashSaplingAnchor; + break; + default: + throw std::runtime_error("Unknown shielded type"); + } } void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { @@ -312,10 +426,45 @@ void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNu } } +template +void BatchWriteAnchors( + Map &mapAnchors, + Map &cacheAnchors, + size_t &cachedCoinsUsage +) +{ + for (MapIterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();) + { + if (child_it->second.flags & MapEntry::DIRTY) { + MapIterator parent_it = cacheAnchors.find(child_it->first); + + if (parent_it == cacheAnchors.end()) { + MapEntry& entry = cacheAnchors[child_it->first]; + entry.entered = child_it->second.entered; + entry.tree = child_it->second.tree; + entry.flags = MapEntry::DIRTY; + + cachedCoinsUsage += entry.tree.DynamicMemoryUsage(); + } else { + if (parent_it->second.entered != child_it->second.entered) { + // The parent may have removed the entry. + parent_it->second.entered = child_it->second.entered; + parent_it->second.flags |= MapEntry::DIRTY; + } + } + } + + MapIterator itOld = child_it++; + mapAnchors.erase(itOld); + } +} + bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, - const uint256 &hashAnchorIn, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchorIn, + const uint256 &hashSaplingAnchorIn, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers) { assert(!hasModifier); @@ -354,43 +503,23 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, mapCoins.erase(itOld); } - for (CAnchorsMap::iterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();) - { - if (child_it->second.flags & CAnchorsCacheEntry::DIRTY) { - CAnchorsMap::iterator parent_it = cacheAnchors.find(child_it->first); - - if (parent_it == cacheAnchors.end()) { - CAnchorsCacheEntry& entry = cacheAnchors[child_it->first]; - entry.entered = child_it->second.entered; - entry.tree = child_it->second.tree; - entry.flags = CAnchorsCacheEntry::DIRTY; - - cachedCoinsUsage += entry.tree.DynamicMemoryUsage(); - } else { - if (parent_it->second.entered != child_it->second.entered) { - // The parent may have removed the entry. - parent_it->second.entered = child_it->second.entered; - parent_it->second.flags |= CAnchorsCacheEntry::DIRTY; - } - } - } - - CAnchorsMap::iterator itOld = child_it++; - mapAnchors.erase(itOld); - } + ::BatchWriteAnchors(mapSproutAnchors, cacheSproutAnchors, cachedCoinsUsage); + ::BatchWriteAnchors(mapSaplingAnchors, cacheSaplingAnchors, cachedCoinsUsage); ::BatchWriteNullifiers(mapSproutNullifiers, cacheSproutNullifiers); ::BatchWriteNullifiers(mapSaplingNullifiers, cacheSaplingNullifiers); - hashAnchor = hashAnchorIn; + hashSproutAnchor = hashSproutAnchorIn; + hashSaplingAnchor = hashSaplingAnchorIn; hashBlock = hashBlockIn; return true; } bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheSproutNullifiers, cacheSaplingNullifiers); + bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, cacheSproutAnchors, cacheSaplingAnchors, cacheSproutNullifiers, cacheSaplingNullifiers); cacheCoins.clear(); - cacheAnchors.clear(); + cacheSproutAnchors.clear(); + cacheSaplingAnchors.clear(); cacheSproutNullifiers.clear(); cacheSaplingNullifiers.clear(); cachedCoinsUsage = 0; @@ -430,7 +559,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const { BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers) { - if (GetNullifier(nullifier, SPROUT_NULLIFIER)) { + if (GetNullifier(nullifier, SPROUT)) { // If the nullifier is set, this transaction // double-spends! return false; @@ -441,7 +570,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const auto it = intermediates.find(joinsplit.anchor); if (it != intermediates.end()) { tree = it->second; - } else if (!GetAnchorAt(joinsplit.anchor, tree)) { + } else if (!GetSproutAnchorAt(joinsplit.anchor, tree)) { return false; } @@ -454,8 +583,13 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const } for (const SpendDescription &spendDescription : tx.vShieldedSpend) { - if (GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER)) // Prevent double spends + if (GetNullifier(spendDescription.nullifier, SAPLING)) // Prevent double spends return false; + + ZCSaplingIncrementalMerkleTree tree; + if (!GetSaplingAnchorAt(spendDescription.anchor, tree)) { + return false; + } } return true; diff --git a/src/coins.h b/src/coins.h index e2b454649..bd5105024 100644 --- a/src/coins.h +++ b/src/coins.h @@ -273,7 +273,7 @@ struct CCoinsCacheEntry CCoinsCacheEntry() : coins(), flags(0) {} }; -struct CAnchorsCacheEntry +struct CAnchorsSproutCacheEntry { bool entered; // This will be false if the anchor is removed from the cache ZCIncrementalMerkleTree tree; // The tree itself @@ -283,7 +283,20 @@ struct CAnchorsCacheEntry DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view. }; - CAnchorsCacheEntry() : entered(false), flags(0) {} + CAnchorsSproutCacheEntry() : entered(false), flags(0) {} +}; + +struct CAnchorsSaplingCacheEntry +{ + bool entered; // This will be false if the anchor is removed from the cache + ZCSaplingIncrementalMerkleTree tree; // The tree itself + unsigned char flags; + + enum Flags { + DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view. + }; + + CAnchorsSaplingCacheEntry() : entered(false), flags(0) {} }; struct CNullifiersCacheEntry @@ -298,14 +311,15 @@ struct CNullifiersCacheEntry CNullifiersCacheEntry() : entered(false), flags(0) {} }; -enum NullifierType +enum ShieldedType { - SPROUT_NULLIFIER, - SAPLING_NULLIFIER, + SPROUT, + SAPLING, }; typedef boost::unordered_map CCoinsMap; -typedef boost::unordered_map CAnchorsMap; +typedef boost::unordered_map CAnchorsSproutMap; +typedef boost::unordered_map CAnchorsSaplingMap; typedef boost::unordered_map CNullifiersMap; struct CCoinsStats @@ -326,11 +340,14 @@ struct CCoinsStats class CCoinsView { public: - //! Retrieve the tree at a particular anchored root in the chain - virtual bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; + //! Retrieve the tree (Sprout) at a particular anchored root in the chain + virtual bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; + + //! Retrieve the tree (Sapling) at a particular anchored root in the chain + virtual bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const; //! Determine whether a nullifier is spent or not - virtual bool GetNullifier(const uint256 &nullifier, NullifierType type) const; + virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; //! Retrieve the CCoins (unspent transaction outputs) for a given txid virtual bool GetCoins(const uint256 &txid, CCoins &coins) const; @@ -343,14 +360,16 @@ public: virtual uint256 GetBestBlock() const; //! Get the current "tip" or the latest anchored tree root in the chain - virtual uint256 GetBestAnchor() const; + virtual uint256 GetBestAnchor(ShieldedType type) const; //! Do a bulk modification (multiple CCoins changes + BestBlock change). //! The passed mapCoins can be modified. virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers); @@ -370,17 +389,20 @@ protected: public: CCoinsViewBacked(CCoinsView *viewIn); - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; - bool GetNullifier(const uint256 &nullifier, NullifierType type) const; + bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; + bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const; + bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - uint256 GetBestAnchor() const; + uint256 GetBestAnchor(ShieldedType type) const; void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers); bool GetStats(CCoinsStats &stats) const; @@ -423,8 +445,10 @@ protected: */ mutable uint256 hashBlock; mutable CCoinsMap cacheCoins; - mutable uint256 hashAnchor; - mutable CAnchorsMap cacheAnchors; + mutable uint256 hashSproutAnchor; + mutable uint256 hashSaplingAnchor; + mutable CAnchorsSproutMap cacheSproutAnchors; + mutable CAnchorsSaplingMap cacheSaplingAnchors; mutable CNullifiersMap cacheSproutNullifiers; mutable CNullifiersMap cacheSaplingNullifiers; @@ -436,28 +460,35 @@ public: ~CCoinsViewCache(); // Standard CCoinsView methods - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; - bool GetNullifier(const uint256 &nullifier, NullifierType type) const; + bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; + bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const; + bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - uint256 GetBestAnchor() const; + uint256 GetBestAnchor(ShieldedType type) const; void SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers); - // Adds the tree to mapAnchors and sets the current commitment + // Adds the tree to mapSproutAnchors and sets the current commitment // root to this root. - void PushAnchor(const ZCIncrementalMerkleTree &tree); + void PushSproutAnchor(const ZCIncrementalMerkleTree &tree); + + // Adds the tree to mapSaplingAnchors and sets the current commitment + // root to this root. + void PushSaplingAnchor(const ZCSaplingIncrementalMerkleTree &tree); // Removes the current commitment root from mapAnchors and sets // the new current root. - void PopAnchor(const uint256 &rt); + void PopAnchor(const uint256 &rt, ShieldedType type); // Marks nullifiers for a given transaction as spent or not. void SetNullifiers(const CTransaction& tx, bool spent); @@ -520,6 +551,31 @@ private: * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache. */ CCoinsViewCache(const CCoinsViewCache &); + + //! Generalized interface for popping anchors + template + void AbstractPopAnchor( + const uint256 &newrt, + ShieldedType type, + Cache &cacheAnchors, + uint256 &hash + ); + + //! Generalized interface for pushing anchors + template + void AbstractPushAnchor( + const Tree &tree, + ShieldedType type, + Cache &cacheAnchors, + uint256 &hash + ); + + //! Interface for bringing an anchor into the cache. + template + void BringBestAnchorIntoCache( + const uint256 ¤tRoot, + Tree &tree + ); }; #endif // BITCOIN_COINS_H diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 981c4eb08..1fd4cd7e7 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -19,11 +19,15 @@ class FakeCoinsViewDB : public CCoinsView { public: FakeCoinsViewDB() {} - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; } - bool GetNullifier(const uint256 &nf, NullifierType type) const { + bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { + return false; + } + + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -47,15 +51,17 @@ public: return a; } - uint256 GetBestAnchor() const { + uint256 GetBestAnchor(ShieldedType type) const { uint256 a; return a; } bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers) { return false; diff --git a/src/gtest/test_merkletree.cpp b/src/gtest/test_merkletree.cpp index b3e1027a1..166bbaf6d 100644 --- a/src/gtest/test_merkletree.cpp +++ b/src/gtest/test_merkletree.cpp @@ -7,6 +7,13 @@ #include "test/data/merkle_path.json.h" #include "test/data/merkle_commitments.json.h" +#include "test/data/merkle_roots_sapling.json.h" +#include "test/data/merkle_roots_empty_sapling.json.h" +#include "test/data/merkle_serialization_sapling.json.h" +#include "test/data/merkle_witness_serialization_sapling.json.h" +#include "test/data/merkle_path_sapling.json.h" +#include "test/data/merkle_commitments_sapling.json.h" + #include #include @@ -51,7 +58,8 @@ void test_tree( UniValue root_tests, UniValue ser_tests, UniValue witness_ser_tests, - UniValue path_tests + UniValue path_tests, + bool libsnark_test ) { size_t witness_ser_i = 0; @@ -106,10 +114,9 @@ void test_tree( ASSERT_THROW(wit.element(), std::runtime_error); } else { auto path = wit.path(); + expect_test_vector(path_tests[path_i++], path); - { - expect_test_vector(path_tests[path_i++], path); - + if (libsnark_test) { typedef Fr FieldT; protoboard pb; @@ -188,7 +195,31 @@ TEST(merkletree, vectors) { UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path)); UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments)); - test_tree(commitment_tests, root_tests, ser_tests, witness_ser_tests, path_tests); + test_tree( + commitment_tests, + root_tests, + ser_tests, + witness_ser_tests, + path_tests, + true + ); +} + +TEST(merkletree, SaplingVectors) { + UniValue root_tests = read_json(MAKE_STRING(json_tests::merkle_roots_sapling)); + UniValue ser_tests = read_json(MAKE_STRING(json_tests::merkle_serialization_sapling)); + UniValue witness_ser_tests = read_json(MAKE_STRING(json_tests::merkle_witness_serialization_sapling)); + UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path_sapling)); + UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments_sapling)); + + test_tree( + commitment_tests, + root_tests, + ser_tests, + witness_ser_tests, + path_tests, + false + ); } TEST(merkletree, emptyroots) { @@ -204,6 +235,19 @@ TEST(merkletree, emptyroots) { ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 64); } +TEST(merkletree, EmptyrootsSapling) { + UniValue empty_roots = read_json(MAKE_STRING(json_tests::merkle_roots_empty_sapling)); + + libzcash::EmptyMerkleRoots<62, libzcash::PedersenHash> emptyroots; + + for (size_t depth = 0; depth <= 62; depth++) { + expect_test_vector(empty_roots[depth], emptyroots.empty_root(depth)); + } + + // Double check that we're testing (at least) all the empty roots we'll use. + ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 62); +} + TEST(merkletree, emptyroot) { // This literal is the depth-20 empty tree root with the bytes reversed to // account for the fact that uint256S() loads a big-endian representation of @@ -213,6 +257,15 @@ TEST(merkletree, emptyroot) { ASSERT_TRUE(ZCIncrementalMerkleTree::empty_root() == expected); } +TEST(merkletree, EmptyrootSapling) { + // This literal is the depth-20 empty tree root with the bytes reversed to + // account for the fact that uint256S() loads a big-endian representation of + // an integer which converted to little-endian internally. + uint256 expected = uint256S("427719cde12e9ef88a2811be36a0ef15018c7674dc8faa76ace727fdbc09af6a"); + + ASSERT_TRUE(ZCSaplingIncrementalMerkleTree::empty_root() == expected); +} + TEST(merkletree, deserializeInvalid) { // attempt to deserialize a small tree from a serialized large tree // (exceeds depth well-formedness check) diff --git a/src/gtest/test_pedersen_hash.cpp b/src/gtest/test_pedersen_hash.cpp index 7a9eb04fd..608d8b448 100644 --- a/src/gtest/test_pedersen_hash.cpp +++ b/src/gtest/test_pedersen_hash.cpp @@ -3,13 +3,13 @@ #include "uint256.h" TEST(PedersenHash, TestAPI) { - const uint256 a = uint256S("0acaa62d40fcdd9192ed35ea9df31660ccf7f6c60566530faaa444fb5d0d410e"); - const uint256 b = uint256S("6041357de59ba64959d1b60f93de24dfe5ea1e26ed9e8a73d35b225a1845ba70"); + const uint256 a = uint256S("7082b0badf222555f0ca66a0636fef330668cfb957acb74989bb3f02b4655350"); + const uint256 b = uint256S("0e5da2185a44dacbce5179b7647857a7fb247bc9f06d8b9a9265d9a7beac8206"); uint256 result; librustzcash_merkle_hash(25, a.begin(), b.begin(), result.begin()); - uint256 expected_result = uint256S("4253b36834b3f64cc6182f1816911e1c9460cb88afeafb155244dd0038ad4717"); + uint256 expected_result = uint256S("e8e2b51c00bb224aa8df57f511d306293878896818f41863326fcd2c16cdca42"); ASSERT_TRUE(result == expected_result); } diff --git a/src/gtest/test_validation.cpp b/src/gtest/test_validation.cpp index 710e3c600..8609c93ba 100644 --- a/src/gtest/test_validation.cpp +++ b/src/gtest/test_validation.cpp @@ -21,11 +21,15 @@ class FakeCoinsViewDB : public CCoinsView { public: FakeCoinsViewDB() {} - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { + bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; } - bool GetNullifier(const uint256 &nf, NullifierType type) const { + bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { + return false; + } + + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -42,15 +46,17 @@ public: return a; } - uint256 GetBestAnchor() const { + uint256 GetBestAnchor(ShieldedType type) const { uint256 a; return a; } bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap saplingNullifiersMap) { return false; diff --git a/src/main.cpp b/src/main.cpp index e4c9e12f8..f6c211c3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1306,13 +1306,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - if (pool.nullifierExists(nf, SPROUT_NULLIFIER)) { + if (pool.nullifierExists(nf, SPROUT)) { return false; } } } for (const SpendDescription &spendDescription : tx.vShieldedSpend) { - if (pool.nullifierExists(spendDescription.nullifier, SAPLING_NULLIFIER)) { + if (pool.nullifierExists(spendDescription.nullifier, SAPLING)) { return false; } } @@ -2150,8 +2150,19 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } - // set the old best anchor back - view.PopAnchor(blockUndo.old_tree_root); + // set the old best Sprout anchor back + view.PopAnchor(blockUndo.old_sprout_tree_root, SPROUT); + + // set the old best Sapling anchor back + // We can get this from the `hashFinalSaplingRoot` of the last block + // However, this is only reliable if the last block was on or after + // the Sapling activation height. Otherwise, the last anchor was the + // empty root. + if (NetworkUpgradeActive(pindex->pprev->nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { + view.PopAnchor(pindex->pprev->hashFinalSaplingRoot, SAPLING); + } else { + view.PopAnchor(ZCSaplingIncrementalMerkleTree::empty_root(), SAPLING); + } // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); @@ -2296,9 +2307,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin view.SetBestBlock(pindex->GetBlockHash()); // Before the genesis block, there was an empty tree ZCIncrementalMerkleTree tree; - pindex->hashAnchor = tree.root(); + pindex->hashSproutAnchor = tree.root(); // The genesis block contained no JoinSplits - pindex->hashAnchorEnd = pindex->hashAnchor; + pindex->hashFinalSproutRoot = pindex->hashSproutAnchor; } return true; } @@ -2331,22 +2342,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Construct the incremental merkle tree at the current // block position, - auto old_tree_root = view.GetBestAnchor(); + auto old_sprout_tree_root = view.GetBestAnchor(SPROUT); // saving the top anchor in the block index as we go. if (!fJustCheck) { - pindex->hashAnchor = old_tree_root; + pindex->hashSproutAnchor = old_sprout_tree_root; } - ZCIncrementalMerkleTree tree; + ZCIncrementalMerkleTree sprout_tree; // This should never fail: we should always be able to get the root // that is on the tip of our chain - assert(view.GetAnchorAt(old_tree_root, tree)); + assert(view.GetSproutAnchorAt(old_sprout_tree_root, sprout_tree)); { // Consistency check: the root of the tree we're given should // match what we asked for. - assert(tree.root() == old_tree_root); + assert(sprout_tree.root() == old_sprout_tree_root); } + ZCSaplingIncrementalMerkleTree sapling_tree; + assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); + // Grab the consensus branch ID for the block's height auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); @@ -2404,19 +2418,34 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) { // Insert the note commitments into our temporary tree. - tree.append(note_commitment); + sprout_tree.append(note_commitment); } } + BOOST_FOREACH(const OutputDescription &outputDescription, tx.vShieldedOutput) { + sapling_tree.append(outputDescription.cm); + } + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - view.PushAnchor(tree); + view.PushSproutAnchor(sprout_tree); + view.PushSaplingAnchor(sapling_tree); if (!fJustCheck) { - pindex->hashAnchorEnd = tree.root(); + pindex->hashFinalSproutRoot = sprout_tree.root(); + } + blockundo.old_sprout_tree_root = old_sprout_tree_root; + + // If Sapling is active, block.hashFinalSaplingRoot must be the + // same as the root of the Sapling tree + if (NetworkUpgradeActive(pindex->nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_SAPLING)) { + if (block.hashFinalSaplingRoot != sapling_tree.root()) { + return state.DoS(100, + error("ConnectBlock(): block's hashFinalSaplingRoot is incorrect"), + REJECT_INVALID, "bad-sapling-root-in-block"); + } } - blockundo.old_tree_root = old_tree_root; int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); @@ -2659,7 +2688,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { if (!ReadBlockFromDisk(block, pindexDelete)) return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. - uint256 anchorBeforeDisconnect = pcoinsTip->GetBestAnchor(); + uint256 sproutAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SPROUT); + uint256 saplingAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SAPLING); int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pcoinsTip); @@ -2668,7 +2698,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { assert(view.Flush()); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor(); + uint256 sproutAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SPROUT); + uint256 saplingAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SAPLING); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; @@ -2682,10 +2713,15 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) mempool.remove(tx, removed, true); } - if (anchorBeforeDisconnect != anchorAfterDisconnect) { + if (sproutAnchorBeforeDisconnect != sproutAnchorAfterDisconnect) { // The anchor may not change between block disconnects, // in which case we don't want to evict from the mempool yet! - mempool.removeWithAnchor(anchorBeforeDisconnect); + mempool.removeWithAnchor(sproutAnchorBeforeDisconnect, SPROUT); + } + if (saplingAnchorBeforeDisconnect != saplingAnchorAfterDisconnect) { + // The anchor may not change between block disconnects, + // in which case we don't want to evict from the mempool yet! + mempool.removeWithAnchor(saplingAnchorBeforeDisconnect, SAPLING); } } @@ -2693,7 +2729,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { UpdateTip(pindexDelete->pprev); // Get the current commitment tree ZCIncrementalMerkleTree newTree; - assert(pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), newTree)); + assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), newTree)); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { @@ -2727,7 +2763,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * } // Get the current commitment tree ZCIncrementalMerkleTree oldTree; - assert(pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), oldTree)); + assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), oldTree)); // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; @@ -3928,12 +3964,12 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; // - This relationship will always be true even if pprev has multiple - // children, because hashAnchor is technically a property of pprev, + // children, because hashSproutAnchor is technically a property of pprev, // not its children. // - This will miss chain tips; we handle the best tip below, and other // tips will be handled by ConnectTip during a re-org. if (pindex->pprev) { - pindex->pprev->hashAnchorEnd = pindex->hashAnchor; + pindex->pprev->hashFinalSproutRoot = pindex->hashSproutAnchor; } } @@ -3942,8 +3978,8 @@ bool static LoadBlockIndexDB() if (it == mapBlockIndex.end()) return true; chainActive.SetTip(it->second); - // Set hashAnchorEnd for the end of best chain - it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor(); + // Set hashFinalSproutRoot for the end of best chain + it->second->hashFinalSproutRoot = pcoinsTip->GetBestAnchor(SPROUT); PruneBlockIndexCandidates(); diff --git a/src/miner.cpp b/src/miner.cpp index 44e28f82b..7c6e4140a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -149,6 +149,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); CCoinsViewCache view(pcoinsTip); + ZCSaplingIncrementalMerkleTree sapling_tree; + assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); + // Priority order to process transactions list vOrphan; // list memory doesn't move map > mapDependers; @@ -301,6 +304,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) UpdateCoins(tx, view, nHeight); + BOOST_FOREACH(const OutputDescription &outDescription, tx.vShieldedOutput) { + sapling_tree.append(outDescription.cm); + } + // Added pblock->vtx.push_back(tx); pblocktemplate->vTxFees.push_back(nTxFees); @@ -374,7 +381,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->hashReserved = uint256(); + pblock->hashFinalSaplingRoot = sapling_tree.root(); UpdateTime(pblock, Params().GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); pblock->nSolution.clear(); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index c2300c82a..430ac8721 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -112,12 +112,12 @@ uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector& vMer std::string CBlock::ToString() const { std::stringstream s; - s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashReserved=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n", + s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashFinalSaplingRoot=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n", GetHash().ToString(), nVersion, hashPrevBlock.ToString(), hashMerkleRoot.ToString(), - hashReserved.ToString(), + hashFinalSaplingRoot.ToString(), nTime, nBits, nNonce.ToString(), vtx.size()); for (unsigned int i = 0; i < vtx.size(); i++) diff --git a/src/primitives/block.h b/src/primitives/block.h index f6eeebcbb..489d54711 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -26,7 +26,7 @@ public: int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; - uint256 hashReserved; + uint256 hashFinalSaplingRoot; uint32_t nTime; uint32_t nBits; uint256 nNonce; @@ -44,7 +44,7 @@ public: READWRITE(this->nVersion); READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); - READWRITE(hashReserved); + READWRITE(hashFinalSaplingRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); @@ -56,7 +56,7 @@ public: nVersion = CBlockHeader::CURRENT_VERSION; hashPrevBlock.SetNull(); hashMerkleRoot.SetNull(); - hashReserved.SetNull(); + hashFinalSaplingRoot.SetNull(); nTime = 0; nBits = 0; nNonce = uint256(); @@ -118,7 +118,7 @@ public: block.nVersion = nVersion; block.hashPrevBlock = hashPrevBlock; block.hashMerkleRoot = hashMerkleRoot; - block.hashReserved = hashReserved; + block.hashFinalSaplingRoot = hashFinalSaplingRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; @@ -158,7 +158,7 @@ public: READWRITE(this->nVersion); READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); - READWRITE(hashReserved); + READWRITE(hashFinalSaplingRoot); READWRITE(nTime); READWRITE(nBits); } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index c0565a8fc..89ada7cff 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -155,7 +155,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("bits", strprintf("%08x", block.nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("anchor", blockindex->hashAnchorEnd.GetHex())); + result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex())); UniValue valuePools(UniValue::VARR); valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); @@ -770,7 +770,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) obj.push_back(Pair("pruned", fPruneMode)); ZCIncrementalMerkleTree tree; - pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), tree); + pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); obj.push_back(Pair("commitments", static_cast(tree.size()))); CBlockIndex* tip = chainActive.Tip(); diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index ae147ea08..c1bd8a553 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -25,26 +25,29 @@ namespace class CCoinsViewTest : public CCoinsView { uint256 hashBestBlock_; - uint256 hashBestAnchor_; + uint256 hashBestSproutAnchor_; + uint256 hashBestSaplingAnchor_; std::map map_; - std::map mapAnchors_; + std::map mapSproutAnchors_; + std::map mapSaplingAnchors_; std::map mapSproutNullifiers_; std::map mapSaplingNullifiers_; public: CCoinsViewTest() { - hashBestAnchor_ = ZCIncrementalMerkleTree::empty_root(); + hashBestSproutAnchor_ = ZCIncrementalMerkleTree::empty_root(); + hashBestSaplingAnchor_ = ZCSaplingIncrementalMerkleTree::empty_root(); } - bool GetAnchorAt(const uint256& rt, ZCIncrementalMerkleTree &tree) const { + bool GetSproutAnchorAt(const uint256& rt, ZCIncrementalMerkleTree &tree) const { if (rt == ZCIncrementalMerkleTree::empty_root()) { ZCIncrementalMerkleTree new_tree; tree = new_tree; return true; } - std::map::const_iterator it = mapAnchors_.find(rt); - if (it == mapAnchors_.end()) { + std::map::const_iterator it = mapSproutAnchors_.find(rt); + if (it == mapSproutAnchors_.end()) { return false; } else { tree = it->second; @@ -52,18 +55,34 @@ public: } } - bool GetNullifier(const uint256 &nf, NullifierType type) const + bool GetSaplingAnchorAt(const uint256& rt, ZCSaplingIncrementalMerkleTree &tree) const { + if (rt == ZCSaplingIncrementalMerkleTree::empty_root()) { + ZCSaplingIncrementalMerkleTree new_tree; + tree = new_tree; + return true; + } + + std::map::const_iterator it = mapSaplingAnchors_.find(rt); + if (it == mapSaplingAnchors_.end()) { + return false; + } else { + tree = it->second; + return true; + } + } + + bool GetNullifier(const uint256 &nf, ShieldedType type) const { const std::map* mapToUse; switch (type) { - case SPROUT_NULLIFIER: + case SPROUT: mapToUse = &mapSproutNullifiers_; break; - case SAPLING_NULLIFIER: + case SAPLING: mapToUse = &mapSaplingNullifiers_; break; default: - throw std::runtime_error("Unknown nullifier type"); + throw std::runtime_error("Unknown shielded type"); } std::map::const_iterator it = mapToUse->find(nf); if (it == mapToUse->end()) { @@ -75,7 +94,18 @@ public: } } - uint256 GetBestAnchor() const { return hashBestAnchor_; } + uint256 GetBestAnchor(ShieldedType type) const { + switch (type) { + case SPROUT: + return hashBestSproutAnchor_; + break; + case SAPLING: + return hashBestSaplingAnchor_; + break; + default: + throw std::runtime_error("Unknown shielded type"); + } + } bool GetCoins(const uint256& txid, CCoins& coins) const { @@ -114,8 +144,10 @@ public: bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, - const uint256& hashAnchor, - CAnchorsMap& mapAnchors, + const uint256& hashSproutAnchor, + const uint256& hashSaplingAnchor, + CAnchorsSproutMap& mapSproutAnchors, + CAnchorsSaplingMap& mapSaplingAnchors, CNullifiersMap& mapSproutNullifiers, CNullifiersMap& mapSaplingNullifiers) { @@ -127,25 +159,38 @@ public: } mapCoins.erase(it++); } - for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end(); ) { + for (CAnchorsSproutMap::iterator it = mapSproutAnchors.begin(); it != mapSproutAnchors.end(); ) { if (it->second.entered) { std::map::iterator ret = - mapAnchors_.insert(std::make_pair(it->first, ZCIncrementalMerkleTree())).first; + mapSproutAnchors_.insert(std::make_pair(it->first, ZCIncrementalMerkleTree())).first; ret->second = it->second.tree; } else { - mapAnchors_.erase(it->first); + mapSproutAnchors_.erase(it->first); } - mapAnchors.erase(it++); + mapSproutAnchors.erase(it++); + } + for (CAnchorsSaplingMap::iterator it = mapSaplingAnchors.begin(); it != mapSaplingAnchors.end(); ) { + if (it->second.entered) { + std::map::iterator ret = + mapSaplingAnchors_.insert(std::make_pair(it->first, ZCSaplingIncrementalMerkleTree())).first; + + ret->second = it->second.tree; + } else { + mapSaplingAnchors_.erase(it->first); + } + mapSaplingAnchors.erase(it++); } BatchWriteNullifiers(mapSproutNullifiers, mapSproutNullifiers_); BatchWriteNullifiers(mapSaplingNullifiers, mapSaplingNullifiers_); mapCoins.clear(); - mapAnchors.clear(); + mapSproutAnchors.clear(); + mapSaplingAnchors.clear(); hashBestBlock_ = hashBlock; - hashBestAnchor_ = hashAnchor; + hashBestSproutAnchor_ = hashSproutAnchor; + hashBestSaplingAnchor_ = hashSaplingAnchor; return true; } @@ -161,7 +206,8 @@ public: { // Manually recompute the dynamic usage of the whole data, and compare it. size_t ret = memusage::DynamicUsage(cacheCoins) + - memusage::DynamicUsage(cacheAnchors) + + memusage::DynamicUsage(cacheSproutAnchors) + + memusage::DynamicUsage(cacheSaplingAnchors) + memusage::DynamicUsage(cacheSproutNullifiers) + memusage::DynamicUsage(cacheSaplingNullifiers); for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) { @@ -215,11 +261,11 @@ BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup) void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) { // Make sure the nullifiers have not gotten mixed up - BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING_NULLIFIER)); - BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT_NULLIFIER)); + BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING)); + BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT)); // Check if the nullifiers either are or are not in the cache - bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT_NULLIFIER); - bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING_NULLIFIER); + bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT); + bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING); BOOST_CHECK(containsSproutNullifier == shouldBeInCache); BOOST_CHECK(containsSaplingNullifier == shouldBeInCache); } @@ -325,21 +371,21 @@ BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) tree.append(cm); // Add the anchor - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); // Remove the anchor - cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); cache1.Flush(); // Add the anchor back - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); // The base contains the anchor, of course! { ZCIncrementalMerkleTree checktree; - BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(cache1.GetSproutAnchorAt(tree.root(), checktree)); BOOST_CHECK(checktree.root() == tree.root()); } } @@ -355,15 +401,15 @@ BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) tree.append(cm); // Add the anchor and flush to disk - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); // Remove the anchor, but don't flush yet! - cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); { CCoinsViewCacheTest cache2(&cache1); // Build cache on top - cache2.PushAnchor(tree); // Put the same anchor back! + cache2.PushSproutAnchor(tree); // Put the same anchor back! cache2.Flush(); // Flush to cache1 } @@ -372,7 +418,7 @@ BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) // treestate... { ZCIncrementalMerkleTree checktree; - BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(cache1.GetSproutAnchorAt(tree.root(), checktree)); BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. } @@ -381,7 +427,7 @@ BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) cache1.Flush(); { ZCIncrementalMerkleTree checktree; - BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(cache1.GetSproutAnchorAt(tree.root(), checktree)); BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. } } @@ -398,12 +444,12 @@ BOOST_AUTO_TEST_CASE(anchor_regression_test) ZCIncrementalMerkleTree tree; uint256 cm = GetRandHash(); tree.append(cm); - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); - cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); - BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); - BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); + BOOST_CHECK(cache1.GetBestAnchor(SPROUT) == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetSproutAnchorAt(tree.root(), tree)); } // Also correct behavior: @@ -415,13 +461,13 @@ BOOST_AUTO_TEST_CASE(anchor_regression_test) ZCIncrementalMerkleTree tree; uint256 cm = GetRandHash(); tree.append(cm); - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); - cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); cache1.Flush(); - BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); - BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + BOOST_CHECK(cache1.GetBestAnchor(SPROUT) == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetSproutAnchorAt(tree.root(), tree)); } // Works because we bring the anchor in from parent cache. @@ -433,19 +479,19 @@ BOOST_AUTO_TEST_CASE(anchor_regression_test) ZCIncrementalMerkleTree tree; uint256 cm = GetRandHash(); tree.append(cm); - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); { // Pop anchor. CCoinsViewCacheTest cache2(&cache1); - BOOST_CHECK(cache2.GetAnchorAt(tree.root(), tree)); - cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(cache2.GetSproutAnchorAt(tree.root(), tree)); + cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); cache2.Flush(); } - BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); - BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + BOOST_CHECK(cache1.GetBestAnchor(SPROUT) == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetSproutAnchorAt(tree.root(), tree)); } // Was broken: @@ -457,18 +503,18 @@ BOOST_AUTO_TEST_CASE(anchor_regression_test) ZCIncrementalMerkleTree tree; uint256 cm = GetRandHash(); tree.append(cm); - cache1.PushAnchor(tree); + cache1.PushSproutAnchor(tree); cache1.Flush(); { // Pop anchor. CCoinsViewCacheTest cache2(&cache1); - cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache2.PopAnchor(ZCIncrementalMerkleTree::empty_root(), SPROUT); cache2.Flush(); } - BOOST_CHECK(cache1.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); - BOOST_CHECK(!cache1.GetAnchorAt(tree.root(), tree)); + BOOST_CHECK(cache1.GetBestAnchor(SPROUT) == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(!cache1.GetSproutAnchorAt(tree.root(), tree)); } } @@ -502,22 +548,22 @@ BOOST_AUTO_TEST_CASE(anchors_flush_test) { CCoinsViewCacheTest cache(&base); ZCIncrementalMerkleTree tree; - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree)); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), tree)); appendRandomCommitment(tree); newrt = tree.root(); - cache.PushAnchor(tree); + cache.PushSproutAnchor(tree); cache.Flush(); } { CCoinsViewCacheTest cache(&base); ZCIncrementalMerkleTree tree; - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree)); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), tree)); // Get the cached entry. - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree)); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), tree)); uint256 check_rt = tree.root(); @@ -610,13 +656,13 @@ BOOST_AUTO_TEST_CASE(anchors_test) CCoinsViewTest base; CCoinsViewCacheTest cache(&base); - BOOST_CHECK(cache.GetBestAnchor() == ZCIncrementalMerkleTree::empty_root()); + BOOST_CHECK(cache.GetBestAnchor(SPROUT) == ZCIncrementalMerkleTree::empty_root()); { ZCIncrementalMerkleTree tree; - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree)); - BOOST_CHECK(cache.GetBestAnchor() == tree.root()); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), tree)); + BOOST_CHECK(cache.GetBestAnchor(SPROUT) == tree.root()); appendRandomCommitment(tree); appendRandomCommitment(tree); appendRandomCommitment(tree); @@ -631,12 +677,12 @@ BOOST_AUTO_TEST_CASE(anchors_test) uint256 newrt = tree.root(); uint256 newrt2; - cache.PushAnchor(tree); - BOOST_CHECK(cache.GetBestAnchor() == newrt); + cache.PushSproutAnchor(tree); + BOOST_CHECK(cache.GetBestAnchor(SPROUT) == newrt); { ZCIncrementalMerkleTree confirm_same; - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), confirm_same)); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), confirm_same)); BOOST_CHECK(confirm_same.root() == newrt); } @@ -646,26 +692,26 @@ BOOST_AUTO_TEST_CASE(anchors_test) newrt2 = tree.root(); - cache.PushAnchor(tree); - BOOST_CHECK(cache.GetBestAnchor() == newrt2); + cache.PushSproutAnchor(tree); + BOOST_CHECK(cache.GetBestAnchor(SPROUT) == newrt2); ZCIncrementalMerkleTree test_tree; - BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), test_tree)); + BOOST_CHECK(cache.GetSproutAnchorAt(cache.GetBestAnchor(SPROUT), test_tree)); BOOST_CHECK(tree.root() == test_tree.root()); { ZCIncrementalMerkleTree test_tree2; - cache.GetAnchorAt(newrt, test_tree2); + cache.GetSproutAnchorAt(newrt, test_tree2); BOOST_CHECK(test_tree2.root() == newrt); } { - cache.PopAnchor(newrt); + cache.PopAnchor(newrt, SPROUT); ZCIncrementalMerkleTree obtain_tree; - assert(!cache.GetAnchorAt(newrt2, obtain_tree)); // should have been popped off - assert(cache.GetAnchorAt(newrt, obtain_tree)); + assert(!cache.GetSproutAnchorAt(newrt2, obtain_tree)); // should have been popped off + assert(cache.GetSproutAnchorAt(newrt, obtain_tree)); assert(obtain_tree.root() == newrt); } diff --git a/src/test/data/merkle_commitments_sapling.json b/src/test/data/merkle_commitments_sapling.json new file mode 100644 index 000000000..2303b5026 --- /dev/null +++ b/src/test/data/merkle_commitments_sapling.json @@ -0,0 +1,18 @@ +[ + "556f3af94225d46b1ef652abc9005dee873b2e245eef07fd5be587e0f21023b0", + "d814b127a6c6b8f07ed03f0f6e2843ff04c9851ff824a4e5b4dad5b5f3475722", + "ec030e6d7460f91668cc842ceb78cdb54470469e78cd59cf903d3a6e1aa03e7c", + "b0a0d08406b9e3693ee4c062bd1e6816f95bf14f5a13aafa1d57942c6c1d4250", + "92fc3e7298eb327a88abcc406fbe595e45dddd9b4209803b2e0baa3a8663ecaa", + "f607dd230ada93d14f4de1d9008a5e64a59af87c2e4f64a5f9e55e0cd44867f8", + "ae0bfc1e123edcb6252251611650f3667371f781b60302385c414716c75e8abc", + "91a5e54bf9a9b57e1c163904999ad1527f1e126c685111e18193decca2dd1ada", + "c674f7836089063143fc18b673b2d92f888c63380e3680385d47bcdbd5fe273a", + "7c1dbdb260441b89a08ba411d5f8406e81abd9dc85382f307999fdf77d8fcac8", + "02372c746664e0898576972ca6d0500c7c8ec42f144622349d133b06e837faf0", + "08c6d7dd3d2e387f7b84d6769f2b6cbe308918ab81e0f7321bd0945868d7d4e6", + "a6e8c4061f2ad984d19f2c0a4436b9800e529069c0b0d3186d4683e83bb7eb8c", + "837cc2391338956026521beca5c81b541b7f2d1ead7758bf4d1588dbbcb8fa22", + "1cc467cfd2b504e156c9a38bc5c0e4f5ea6cc208054d2d0653a7e561ac3a3ef4", + "15ac4057a9a94536eca9802de65e985319e89627c9c64bc94626b712bc61363a" +] diff --git a/src/test/data/merkle_path_sapling.json b/src/test/data/merkle_path_sapling.json new file mode 100644 index 000000000..847625164 --- /dev/null +++ b/src/test/data/merkle_path_sapling.json @@ -0,0 +1,122 @@ +[ + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620fe60df1ce575aaab663d158a20f9e560f7bdc5179f1527b3a33e4f587b44ffbc20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620c054f0548d44fb121f3e22fe87517c806628adfd0f3cddc67c982f4adc26b62a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620c054f0548d44fb121f3e22fe87517c806628adfd0f3cddc67c982f4adc26b62a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c216206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096200504f659ffb9358e5fba924944396b345006a0abc2cbfd207c97d6b2323bdc2020cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096200504f659ffb9358e5fba924944396b345006a0abc2cbfd207c97d6b2323bdc2020cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096200504f659ffb9358e5fba924944396b345006a0abc2cbfd207c97d6b2323bdc20206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096200504f659ffb9358e5fba924944396b345006a0abc2cbfd207c97d6b2323bdc20206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096207315163946951eb4895ff12f6b0724c6062f021e669b99fed24dfacd28c9c04420cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096207315163946951eb4895ff12f6b0724c6062f021e669b99fed24dfacd28c9c04420cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096207315163946951eb4895ff12f6b0724c6062f021e669b99fed24dfacd28c9c044206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096207315163946951eb4895ff12f6b0724c6062f021e669b99fed24dfacd28c9c044206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620fe60df1ce575aaab663d158a20f9e560f7bdc5179f1527b3a33e4f587b44ffbc20f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620c4952dd79bbb887a721fde83eeb847a32117311e7dd0d107625ab11063b7b7aa20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620c4952dd79bbb887a721fde83eeb847a32117311e7dd0d107625ab11063b7b7aa20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620c4952dd79bbb887a721fde83eeb847a32117311e7dd0d107625ab11063b7b7aa206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620c4952dd79bbb887a721fde83eeb847a32117311e7dd0d107625ab11063b7b7aa206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620c4a10647b2ed9dfd93323f5b2044550c5763cf198da5297d1778c894337253c620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620c4a10647b2ed9dfd93323f5b2044550c5763cf198da5297d1778c894337253c620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "0420c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff09620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "04203f117e549d8140c4bd97b3d8d73dfeaaa458491730e870e7fd4a3d472493c31c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "04206eb318a627c0ff6ffcef86f2353dcec1c7a51aaea37aa43182020fc06c1df9da20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620fe60df1ce575aaab663d158a20f9e560f7bdc5179f1527b3a33e4f587b44ffbc20c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb648201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb648201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb648201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb648201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb64820197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb64820197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb64820197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "042078bbb59b43ce3baff1db1248b4493bc9bd4dd6c1a3b62b1d0d0a5433b0abb64820197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620c91d1d80ecb6cd0be622f9c277c61d19530c86abcc673b93c279abd8384d787620c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620c91d1d80ecb6cd0be622f9c277c61d19530c86abcc673b93c279abd8384d7876203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d04260201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d04260201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d04260201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d04260201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d0426020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d0426020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d0426020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "04208c026cdb0a983dec0d6a34f397abc8f0839514454f87632ce6d8102c25d0426020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381020c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c21620e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa3810203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c216201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080a00000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "0420677c207a1612b09f2e5d38d372d6e6358bc7223a6c7da695d602501e7a01033c20197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4207417b3f92f30771c261160269f694ec7c14b21cf14e01735460e9e9b47f6dffc20e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381020c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4207417b3f92f30771c261160269f694ec7c14b21cf14e01735460e9e9b47f6dffc20e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa3810203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4207417b3f92f30771c261160269f694ec7c14b21cf14e01735460e9e9b47f6dffc201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080a00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4207417b3f92f30771c261160269f694ec7c14b21cf14e01735460e9e9b47f6dffc201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020b00000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c4201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c4201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c4201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c4201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c420197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c420197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c420197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "042020307a3cff449ca02018e525dbd37e2180b023dbe68ffbff1218f0f1149f12c420197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201ef29413a2eb9b0aea89d61def9e74f61453bc5b8a2799e86020ec1468c71c7820e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381020c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201ef29413a2eb9b0aea89d61def9e74f61453bc5b8a2799e86020ec1468c71c7820e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa3810203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201ef29413a2eb9b0aea89d61def9e74f61453bc5b8a2799e86020ec1468c71c78201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080a00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4201ef29413a2eb9b0aea89d61def9e74f61453bc5b8a2799e86020ec1468c71c78201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020b00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a20fe60df1ce575aaab663d158a20f9e560f7bdc5179f1527b3a33e4f587b44ffbc2022fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c830c00000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e37694636201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e37694636201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e37694636201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e37694636201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e3769463620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e3769463620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e3769463620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "04207493b4b9a35a32120f630e420efc048a71c0d73d6910561e8ba6076e3769463620197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4209c308bfafdf79fafdad22d765cdc484c4c3360878d2de6d3cd95069a9db58c6a20e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381020c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4209c308bfafdf79fafdad22d765cdc484c4c3360878d2de6d3cd95069a9db58c6a20e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa3810203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4209c308bfafdf79fafdad22d765cdc484c4c3360878d2de6d3cd95069a9db58c6a201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080a00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4209c308bfafdf79fafdad22d765cdc484c4c3360878d2de6d3cd95069a9db58c6a201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020b00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a2024714e547866d846699654cf8ffa6245589f518fc575a833966236cd38ce2a0a2022fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c830c00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a2024714e547866d846699654cf8ffa6245589f518fc575a833966236cd38ce2a0a208cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60d00000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d40800201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80000000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d40800201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e20cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a20b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550100000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d40800201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca2050421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00200000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d40800201dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e206a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca207c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0300000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60400000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e620022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142620aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920500000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910600000000000000", + "0420920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080020197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e62065d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3420bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0700000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4208850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0220e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381020c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0800000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4208850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0220e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa3810203a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60900000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4208850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae02201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080a00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4208850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae02201be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda20f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020b00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a20bbb52416fefd25274a8a4e9513319cd7b85fa96244796ff402755618d717a1b02022fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c830c00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a20bbb52416fefd25274a8a4e9513319cd7b85fa96244796ff402755618d717a1b0208cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60d00000000000000", + "04206de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac420d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a206ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e203a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac150e00000000000000" +] diff --git a/src/test/data/merkle_roots_empty_sapling.json b/src/test/data/merkle_roots_empty_sapling.json new file mode 100644 index 000000000..1357a463a --- /dev/null +++ b/src/test/data/merkle_roots_empty_sapling.json @@ -0,0 +1,65 @@ +[ + "8000000000000000000000000000000000000000000000000000000000000000", + "fe60df1ce575aaab663d158a20f9e560f7bdc5179f1527b3a33e4f587b44ffbc", + "1f2610f0650d97f4ecde80d8dab101794631236623c51df290320b2a4c18c216", + "c22d0dcb70b0a84c3440f839a64c9e5259066fff49f6c96a2068bb7fa70ff096", + "52e5469fe719b6afe793c04001a3d92b5af5fcfdf8ba48be2b525ba2db9bf512", + "30560b31aabdc42d32187b57a8c5ed1a0705f0c4079c04fe0c2b70c86612664a", + "056aa19504cf135de6edce4385c447a52888e39dc3816dd29ce85e5cc47d1fd4", + "ff80c661d1fc13591c8d94bd5e5b479efda5ad4e4dc2c232e2c0861903abe716", + "daf8231d59386bd3e9251f010bbe38f44c52275364582f0a1e37200e743e3d56", + "bf14721cc3074ed98066b6eb5f4d83d4a19194915d82ea17e84769d42a62d432", + "7e032b38db8a2e7f9656907f6f74fb7dda94f1d9738c602fbf088d96ac531fc4", + "125abf9164a1f289b4c500c1d871fc88be31c05ad18fd2d96aec2666ed4b70e8", + "69fdd736cce22c44a1050f58fbf4d97db711161302615f46a3d2376a7fdcf556", + "f183596d48ace7d5fe015614c0e466853552cd0badfe4a7e06e387768810b734", + "cd2470c445ae60707986368f7d9db4b34215fb07f6f589677e620c2f42e5840a", + "dcd9f71637d61be27d7b0d0dccd89f9d83de33125b398a686fd05b7eed566026", + "b0152c9b87ce45a02011ed82bbbfce63fb25ae48dbd8cda2fb8cb88d5c2e45c2", + "171bdcbad563410aa9b04ba1fc735151417d450413ae6beef96132bf7310804c", + "a543382d65c96824096b2e625070c7304e2a178a2652a3f0b802bcd9b6ba9f34", + "7f1b88575e956c7332291accb93c9ec53807ca3f511c7699b0bed7b220625112", + "aab00f50fa8f16823bcb954c390ea36950d1e155d19b3cd654781892ac9f1b18", + "73896fbbeea33cf50e1983c24b9395637bcbd1aae6a80675118e1fcc34c4999c", + "e198373975b7953b97d3a105146035492b85dc846f23fe027badd917707b5750", + "e8cede5b9bd42b85d0b188c252bcd3e51c0347b9da6d467d46c0d084db29049c", + "a16cfc7bd8c30be41d110dfc3dd924b05c6b8760e9383a032ef171b2b48ee376", + "448eafd2ff159deae09074cac05dad691b583720c32cf832a16d3d476727bab0", + "5b695b68bd8aa44169d65cdba5ba214cbfb7acf3be934cc147884416ee976ea6", + "41443e9afbf3289a9249f8234ce1f5d2f39ad80e6bf2251d5f4f41bdaf74fe78", + "97c3ab5715d6abd6bdca6854005b6937445b2e13b9adc9dcb6088e9e09681e54", + "9a892c8bfb1327c1f0080b9df0a00ccc5d80e9097c552ef877dec16d68b2a2fc", + "116b047f5a11972b57b36f67e06b3afc010f50e97e6621d744a82dabc7da770a", + "885d2752a3eea80578daff0d588b06077ad1301de5e87209b0714d36d68ab22a", + "6aaf09bcfd27e7ac76aa8fdc74768c0115efa036be11288af89e2ee1cd197742", + "97594310912cc3bcb754e3ba8a60ed8d0f50652475c5bd654191c8faee138ab8", + "a7738d727058a69732e6b75036b321395a4c03b5fa8c482434e03642d6eda2a6", + "c3844d5cd1311e13cb78bf621c93aca5981d03caa7c156310e8c3297878eeb8a", + "798c68f9549fadaffa21b6e557c1c5ead3d16d2db3ac3de7f29cfe2f351e2436", + "87b16cdd406cde2693803894071336fb9efbff101fec12c3bec481190ad22b3a", + "6055d5f60d1b202ab6d7a8e2f63fe434e1c2e389262a82e27b31ed07565f60f4", + "6bfbf247dc13e36239738575bafe5cc13fe5b2f505f870f65262101ede3233e8", + "57c269b80810b91891cf716e1857bf53826545e00f79ba4f8b915a2fd6267c38", + "896bc8135fe0783b54e28817e3c7bbc8bfccd1e022a41465f9c201dab1e42ff2", + "40d35aff33fb5e8c384d4c8252b41f71e9d423a548967a09c4f91f3eef2c38c8", + "2803400ed4bad786302917a17420f69e1fbcd23ec296eb6c199911b3c3dcd8fc", + "5bbef9832cec827a42a152cfd890822866063a96b59507573b1a8c2beb3d0bcc", + "71ad975ec2e034795969eeb387c342140c036b34c972011d34fa040a229df3f8", + "53de2c90267917af80d36fd051cdc89e00182a625c466571028aafdec64d79e0", + "9f713993500d3fa1cd16358624a6aa45ad11e479b5c340d09ff5f40e5a35127c", + "054ef1a5769d76af58eb30f0f1c73cd7bac9638fd92cb677ea1e78fba89e1396", + "45d831a19c20a00dface52b15acea0ea2d5cde27e165939d0fe4b9ff4544f618", + "ab33f64bb9c0cd54682f6c4fd6bf4a5e702f3f2f8484e4ebb8f2417ff2ba9260", + "e4cce49ee7b2685d29e29b079f2eafb0bc4ad8c135bb6b37563bc8e92a5dc2bc", + "75629b841de3fabb0798f802797d5384c0b828485d8056c4b1c58d39fefe881c", + "38ea6af92304ca8ed7c57f50a1578722d096df4cba1bd225c79377a2a1fbab3c", + "f300ab0bf7fe78c2f73da8d557a75a89d9c0b0176375d94b69be97af52bb74f2", + "23854c956b5f6ae2ca6b4b64311b62887913b911e65affe4383ff8c8e6387258", + "390b494667d4ada2da0d41d3ff9980cb20326d2c2c348ad6c4a4fbe1ecbad82c", + "b35578a91591060f7b8869f4cfbeb4e7e7b7b2cb62994e26fa7815aaa4811278", + "af7b7d4bf7952008f95387b59366d8730a35b33103d1e59ae3bf37dae1fe3e3a", + "24ba940af944e3d1d0e43a72d7c15694e52f6b0c084f0ed677f4836346d7285a", + "406a0b8e00cb09cef5159d952c01a531d03fdc3026f75ca21429ddc0c924642a", + "87999570ec0ae957e5052c3e3d37a7146ffc8a5cdbd57ebc9862a9e239943222", + "aff7851ce8e5ba4047cc539bcc1b2df062b8b571efeeb49ea023d3433bae8246" +] diff --git a/src/test/data/merkle_roots_sapling.json b/src/test/data/merkle_roots_sapling.json new file mode 100644 index 000000000..441b7eb59 --- /dev/null +++ b/src/test/data/merkle_roots_sapling.json @@ -0,0 +1,18 @@ +[ + "d550175b5487e5ee74fc74703306b372ca50d6a0adc18e3ce5f7aed5951a641c", + "cfa1dadc77cf65b09e19100c26570c80520844167e926bc0314e9d6730c93626", + "7a1afdd852818faae15aed1d36970fadc57ef44c32d8052194549471e2dfd0d8", + "4a649f3597a687b4e254d064c18cd08419424d208abb8e8b7abf46abcdc8492a", + "bd8b624aea2a2883762ff07e0944943542a7de14d6fe9da7c0e2afe55c0da306", + "0daf0cd2fc50eee569b90d94421e78b27f0a2f598c1a833a350c6c075cc4bba2", + "c6f383f0a8fa5127262e2b7602c87a65e4ed9a80aab2f6ee5df6a33fa78a3298", + "d1ae9b7370c5892bfa669915aecade849b01e36689b36bbe1fa46846edacbff0", + "0823f19e48c98093c43b455723b04eb047d2bdebcfbdd22ece8f68cc26ee0b70", + "541d51b310300abd11032f0fbc9dfa389f653e884a81016a3ab482ff30d64420", + "82a9f0ffcf811b98c5ffc4e29acda3d7e5b7834faecc7624bbb2985b949a506a", + "32dbec464312bbf6b5cfaabe5fd913168e27f08f1bae5bd9ec67436c0376b34c", + "12402e3a518b911712e5bcc16ff90ab22bcb6aa6d2aa7100e359b2b6e20bf3aa", + "03ae56fee9cfe40e369d9204402b0b0c7b64c18fbd08abbef8d245cbeaafadb6", + "c99c2d22235998b9f1b34f7b35657294da004c860cea4946f9e143843f837b9c", + "d59c6ef4a7083993049e034fee53bf770c5c7cfd0b9491b1aca2c6471a316dca" +] diff --git a/src/test/data/merkle_serialization_sapling.json b/src/test/data/merkle_serialization_sapling.json new file mode 100644 index 000000000..da1445e67 --- /dev/null +++ b/src/test/data/merkle_serialization_sapling.json @@ -0,0 +1,18 @@ +[ + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f550000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d800", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60803011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "01f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0003016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4", + "01f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c013a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac1503016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4" +] diff --git a/src/test/data/merkle_witness_serialization_sapling.json b/src/test/data/merkle_witness_serialization_sapling.json new file mode 100644 index 000000000..ea936b26b --- /dev/null +++ b/src/test/data/merkle_witness_serialization_sapling.json @@ -0,0 +1,138 @@ +[ + "00000001b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5500", + "00000002b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d800", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000001225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d800", + "00000002b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d801017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000001225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d801017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8000001017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0000", + "00000003b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000002225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80001cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a00", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b000", + "00000003b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000002225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80001cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca000101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc920000", + "00000003b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000002225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80001cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca000101aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f600", + "00000003b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00010165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e34", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000002225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00010165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e34", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80001cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a0101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00010165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e34", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b00101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00010165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e34", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca000101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00010165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e34", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f60101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0000", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6000101bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae0000", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e00", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e00", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e00", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142600", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142600", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a59100", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a59101013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e60001013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c60000", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a59101013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e60001013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c00", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6000101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020001011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020000", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4000101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020000", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6000101f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7ce184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381000", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381000", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60800", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a59101018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e60001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7ce184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60000", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60000", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60000", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60803011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac40001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60000", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e01018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd56142601018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a59101018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e60001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83020001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7ce184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c8300", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa381001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c8300", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60801018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c8300", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60803011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac40001018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c8300", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac40122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c8300", + "00000004b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000003225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80002cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0250421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca011dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e0101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd5614260101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a5910101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e6000101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0002016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7ce184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa38100101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0001016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa38100101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0001016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6080101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0001016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60803011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4000101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0001016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac40122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c830101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0000", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4000101f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0000", + "00000005b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f55000004225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d8cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01b02310f2e087e55bfd07ef5e242e3b87ee5d00c9ab52f61e6bd42542f93a6f5501225747f3b5d5dab4e5a424f81f85c904ff43286e0f3fd07ef0b8c6a627b114d80003cb37f4591361cd846141e7852c3726b37dd9b011cc55eef2ac931dfe769ff55a1dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca0350421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b01dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "017c3ea01a6e3a3d90cf59cd789e467044b5cd78eb2c84cc6816f960746d0e03ec0150421d6c2c94571dfaaa135a4ff15bf916681ebd62c0e43e69e3b90684d0a0b001016a0fa2d0c5c7b8236092de86d99068ce04511d5adbdd848ccf7c969dbf7338ca021dab0949ba4bae1d065b07b0b308e6a197e118f7614caab5f137ad66dfe6ab8e920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9200020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e603f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd561426920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01aaec63863aaa0b2e3b8009429bdddd455e59be6f40ccab887a32eb98723efc9201f86748d40c5ee5f9a5644f2e7cf89aa5645e8a00d9e14d4fd193da0a23dd07f6020001197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602022293d64836b72bc9d1ae36d6e115c8f7cf0a3f5e75d74059f69f81fd561426920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae00020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e602da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "01bc8a5ec71647415c380203b681f7717366f3501661512225b6dc3e121efc0bae01da1adda2ccde9381e11151686c121e7f52d19a990439161c7eb5a9f94be5a591020165d0fa354e4efa95de2eee9329f18bc7db58d03501471348bfdeca4554eb2e3401197fda6b43eb91b445cf8a19541a6b064e9221cf6d43d67987d4cd0c646121e601920d71bf7104c10b4d780495bb772e8d44a507dbf616963a1b3d2410c9d4080000", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c600030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac403c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7ce184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa38108850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0200", + "013a27fed5dbbc475d3880360e38638c882fd9b273b618fc433106896083f774c601c8ca8f7df7fd9979302f3885dcd9ab816e40f8d511a48ba0891b4460b2bd1d7c030000016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402e184db737209fe4b1277cca58b2e6b52c35d3bd71e990ffe0115c4f6bbfa38108850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0200", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c37020003011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac402e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c6088850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0200", + "01f0fa37e8063b139d342246142fc48e7c0c50d0a62c97768589e06466742c370201e6d4d7685894d01b32f7e081ab188930be6c2b9f76d6847b7f382e3dddd7c60803011be5b76ecb233cd650efcff47cd0be43056324a78a48a97df7faec851bc7dcda00016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4018850ac1e804fc8f724dc410aded78f99e07c18000e4bc97458d1c8b6357eae0200", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a600030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac40222fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83bbb52416fefd25274a8a4e9513319cd7b85fa96244796ff402755618d717a1b000", + "018cebb73be883466d18d3b0c06990520e80b936440a2c9fd184d92a1f06c4e8a60122fab8bcdb88154dbf5877ad1e2d7f1b541bc8a5ec1b52266095381339c27c83030001d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac401bbb52416fefd25274a8a4e9513319cd7b85fa96244796ff402755618d717a1b000", + "01f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0003016ceafae082a15d1b4eb70e9eb7a880e5ee2e3d1d04bfdcc03dd73ed2571c6b8e01d04e93b1b3d6620676e56be7175514ebf0bc03a7f5571403d4547e691102b78a016de08a5115fbf297232c7c9aa0f8cb78ee9a7fc467d0dfc1fe889f2f1e340ac4013a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac1500" +] diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 152919510..3f6a0af77 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -260,6 +260,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } */ + // These tests assume null hashFinalSaplingRoot (before Sapling) + pblock->hashFinalSaplingRoot = uint256(); + CValidationState state; BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL)); BOOST_CHECK_MESSAGE(state.IsValid(), state.GetRejectReason()); diff --git a/src/txdb.cpp b/src/txdb.cpp index 2c2f0faaf..0f1382e6c 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -17,7 +17,8 @@ using namespace std; -static const char DB_ANCHOR = 'A'; +static const char DB_SPROUT_ANCHOR = 'A'; +static const char DB_SAPLING_ANCHOR = 'X'; static const char DB_NULLIFIER = 's'; static const char DB_SAPLING_NULLIFIER = 'S'; static const char DB_COINS = 'c'; @@ -26,7 +27,8 @@ static const char DB_TXINDEX = 't'; static const char DB_BLOCK_INDEX = 'b'; static const char DB_BEST_BLOCK = 'B'; -static const char DB_BEST_ANCHOR = 'a'; +static const char DB_BEST_SPROUT_ANCHOR = 'a'; +static const char DB_BEST_SAPLING_ANCHOR = 'x'; static const char DB_FLAG = 'F'; static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; @@ -40,30 +42,42 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(Get } -bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { +bool CCoinsViewDB::GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { if (rt == ZCIncrementalMerkleTree::empty_root()) { ZCIncrementalMerkleTree new_tree; tree = new_tree; return true; } - bool read = db.Read(make_pair(DB_ANCHOR, rt), tree); + bool read = db.Read(make_pair(DB_SPROUT_ANCHOR, rt), tree); return read; } -bool CCoinsViewDB::GetNullifier(const uint256 &nf, NullifierType type) const { +bool CCoinsViewDB::GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const { + if (rt == ZCSaplingIncrementalMerkleTree::empty_root()) { + ZCSaplingIncrementalMerkleTree new_tree; + tree = new_tree; + return true; + } + + bool read = db.Read(make_pair(DB_SAPLING_ANCHOR, rt), tree); + + return read; +} + +bool CCoinsViewDB::GetNullifier(const uint256 &nf, ShieldedType type) const { bool spent = false; char dbChar; switch (type) { - case SPROUT_NULLIFIER: + case SPROUT: dbChar = DB_NULLIFIER; break; - case SAPLING_NULLIFIER: + case SAPLING: dbChar = DB_SAPLING_NULLIFIER; break; default: - throw runtime_error("Unknown nullifier type"); + throw runtime_error("Unknown shielded type"); } return db.Read(make_pair(dbChar, nf), spent); } @@ -83,10 +97,22 @@ uint256 CCoinsViewDB::GetBestBlock() const { return hashBestChain; } -uint256 CCoinsViewDB::GetBestAnchor() const { +uint256 CCoinsViewDB::GetBestAnchor(ShieldedType type) const { uint256 hashBestAnchor; - if (!db.Read(DB_BEST_ANCHOR, hashBestAnchor)) - return ZCIncrementalMerkleTree::empty_root(); + + switch (type) { + case SPROUT: + if (!db.Read(DB_BEST_SPROUT_ANCHOR, hashBestAnchor)) + return ZCIncrementalMerkleTree::empty_root(); + break; + case SAPLING: + if (!db.Read(DB_BEST_SAPLING_ANCHOR, hashBestAnchor)) + return ZCSaplingIncrementalMerkleTree::empty_root(); + break; + default: + throw runtime_error("Unknown shielded type"); + } + return hashBestAnchor; } @@ -105,10 +131,29 @@ void BatchWriteNullifiers(CDBBatch& batch, CNullifiersMap& mapToUse, const char& } } +template +void BatchWriteAnchors(CDBBatch& batch, Map& mapToUse, const char& dbChar) +{ + for (MapIterator it = mapToUse.begin(); it != mapToUse.end();) { + if (it->second.flags & MapEntry::DIRTY) { + if (!it->second.entered) + batch.Erase(make_pair(dbChar, it->first)); + else { + batch.Write(make_pair(dbChar, it->first), it->second.tree); + } + // TODO: changed++? + } + MapIterator itOld = it++; + mapToUse.erase(itOld); + } +} + bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers) { CDBBatch batch(db); @@ -127,26 +172,18 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, mapCoins.erase(itOld); } - for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) { - if (it->second.flags & CAnchorsCacheEntry::DIRTY) { - if (!it->second.entered) - batch.Erase(make_pair(DB_ANCHOR, it->first)); - else { - batch.Write(make_pair(DB_ANCHOR, it->first), it->second.tree); - } - // TODO: changed++? - } - CAnchorsMap::iterator itOld = it++; - mapAnchors.erase(itOld); - } + ::BatchWriteAnchors(batch, mapSproutAnchors, DB_SPROUT_ANCHOR); + ::BatchWriteAnchors(batch, mapSaplingAnchors, DB_SAPLING_ANCHOR); ::BatchWriteNullifiers(batch, mapSproutNullifiers, DB_NULLIFIER); ::BatchWriteNullifiers(batch, mapSaplingNullifiers, DB_SAPLING_NULLIFIER); if (!hashBlock.IsNull()) batch.Write(DB_BEST_BLOCK, hashBlock); - if (!hashAnchor.IsNull()) - batch.Write(DB_BEST_ANCHOR, hashAnchor); + if (!hashSproutAnchor.IsNull()) + batch.Write(DB_BEST_SPROUT_ANCHOR, hashSproutAnchor); + if (!hashSaplingAnchor.IsNull()) + batch.Write(DB_BEST_SAPLING_ANCHOR, hashSaplingAnchor); LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return db.WriteBatch(batch); @@ -284,10 +321,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nFile = diskindex.nFile; pindexNew->nDataPos = diskindex.nDataPos; pindexNew->nUndoPos = diskindex.nUndoPos; - pindexNew->hashAnchor = diskindex.hashAnchor; + pindexNew->hashSproutAnchor = diskindex.hashSproutAnchor; pindexNew->nVersion = diskindex.nVersion; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; - pindexNew->hashReserved = diskindex.hashReserved; + pindexNew->hashFinalSaplingRoot = diskindex.hashFinalSaplingRoot; pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; diff --git a/src/txdb.h b/src/txdb.h index 53f3c31c6..e8d8ed899 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -35,16 +35,19 @@ protected: public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; - bool GetNullifier(const uint256 &nf, NullifierType type) const; + bool GetSproutAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const; + bool GetSaplingAnchorAt(const uint256 &rt, ZCSaplingIncrementalMerkleTree &tree) const; + bool GetNullifier(const uint256 &nf, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - uint256 GetBestAnchor() const; + uint256 GetBestAnchor(ShieldedType type) const; bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, - const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers); bool GetStats(CCoinsStats &stats) const; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 850851aad..703806667 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -206,7 +206,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem } -void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot) +void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot, ShieldedType type) { // If a block is disconnected from the tip, and the root changed, // we must invalidate transactions from the mempool which spend @@ -217,11 +217,26 @@ void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot) for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction& tx = it->GetTx(); - BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { - if (joinsplit.anchor == invalidRoot) { - transactionsToRemove.push_back(tx); - break; - } + switch (type) { + case SPROUT: + BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) { + if (joinsplit.anchor == invalidRoot) { + transactionsToRemove.push_back(tx); + break; + } + } + break; + case SAPLING: + BOOST_FOREACH(const SpendDescription& spendDescription, tx.vShieldedSpend) { + if (spendDescription.anchor == invalidRoot) { + transactionsToRemove.push_back(tx); + break; + } + } + break; + default: + throw runtime_error("Unknown shielded type"); + break; } } @@ -394,7 +409,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - assert(!pcoins->GetNullifier(nf, SPROUT_NULLIFIER)); + assert(!pcoins->GetNullifier(nf, SPROUT)); } ZCIncrementalMerkleTree tree; @@ -402,7 +417,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (it != intermediates.end()) { tree = it->second; } else { - assert(pcoins->GetAnchorAt(joinsplit.anchor, tree)); + assert(pcoins->GetSproutAnchorAt(joinsplit.anchor, tree)); } BOOST_FOREACH(const uint256& commitment, joinsplit.commitments) @@ -413,7 +428,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const intermediates.insert(std::make_pair(tree.root(), tree)); } for (const SpendDescription &spendDescription : tx.vShieldedSpend) { - assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING_NULLIFIER)); + ZCSaplingIncrementalMerkleTree tree; + + assert(pcoins->GetSaplingAnchorAt(spendDescription.anchor, tree)); + assert(!pcoins->GetNullifier(spendDescription.nullifier, SAPLING)); } if (fDependsWait) waitingOnDependants.push_back(&(*it)); @@ -452,21 +470,21 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(it->first == it->second.ptx->vin[it->second.n].prevout); } - checkNullifiers(SPROUT_NULLIFIER); - checkNullifiers(SAPLING_NULLIFIER); + checkNullifiers(SPROUT); + checkNullifiers(SAPLING); assert(totalTxSize == checkTotal); assert(innerUsage == cachedInnerUsage); } -void CTxMemPool::checkNullifiers(NullifierType type) const +void CTxMemPool::checkNullifiers(ShieldedType type) const { const std::map* mapToUse; switch (type) { - case SPROUT_NULLIFIER: + case SPROUT: mapToUse = &mapSproutNullifiers; break; - case SAPLING_NULLIFIER: + case SAPLING: mapToUse = &mapSaplingNullifiers; break; default: @@ -582,12 +600,12 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const return true; } -bool CTxMemPool::nullifierExists(const uint256& nullifier, NullifierType type) const +bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) const { switch (type) { - case SPROUT_NULLIFIER: + case SPROUT: return mapSproutNullifiers.count(nullifier); - case SAPLING_NULLIFIER: + case SAPLING: return mapSaplingNullifiers.count(nullifier); default: throw runtime_error("Unknown nullifier type"); @@ -596,7 +614,7 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, NullifierType type) c CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } -bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, NullifierType type) const +bool CCoinsViewMemPool::GetNullifier(const uint256 &nf, ShieldedType type) const { return mempool.nullifierExists(nf, type) || base->GetNullifier(nf, type); } diff --git a/src/txmempool.h b/src/txmempool.h index 0397c6d7f..ec8a8518a 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -134,7 +134,7 @@ private: std::map mapSproutNullifiers; std::map mapSaplingNullifiers; - void checkNullifiers(NullifierType type) const; + void checkNullifiers(ShieldedType type) const; public: typedef boost::multi_index_container< @@ -169,7 +169,7 @@ public: bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); - void removeWithAnchor(const uint256 &invalidRoot); + void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx, std::list& removed); void removeExpired(unsigned int nBlockHeight); @@ -192,7 +192,7 @@ public: void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta); void ClearPrioritisation(const uint256 hash); - bool nullifierExists(const uint256& nullifier, NullifierType type) const; + bool nullifierExists(const uint256& nullifier, ShieldedType type) const; unsigned long size() { @@ -243,7 +243,7 @@ protected: public: CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn); - bool GetNullifier(const uint256 &txid, NullifierType type) const; + bool GetNullifier(const uint256 &txid, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; }; diff --git a/src/undo.h b/src/undo.h index e01814e72..fbb350e60 100644 --- a/src/undo.h +++ b/src/undo.h @@ -67,14 +67,14 @@ class CBlockUndo { public: std::vector vtxundo; // for all but the coinbase - uint256 old_tree_root; + uint256 old_sprout_tree_root; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(vtxundo); - READWRITE(old_tree_root); + READWRITE(old_sprout_tree_root); } }; diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index cae42019a..916ad862b 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -429,7 +429,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() auto it = intermediates.find(prevJoinSplit.anchor); if (it != intermediates.end()) { tree = it->second; - } else if (!pcoinsTip->GetAnchorAt(prevJoinSplit.anchor, tree)) { + } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); } @@ -693,7 +693,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInf uint256 anchor; { LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(); // As there are no inputs, ask the wallet for the best anchor + anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor } return perform_joinsplit(info, witnesses, anchor); } diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 73a9360d7..ba9f566f6 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -545,7 +545,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { auto it = intermediates.find(prevJoinSplit.anchor); if (it != intermediates.end()) { tree = it->second; - } else if (!pcoinsTip->GetAnchorAt(prevJoinSplit.anchor, tree)) { + } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); } @@ -914,7 +914,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info uint256 anchor; { LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(); // As there are no inputs, ask the wallet for the best anchor + anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor } return perform_joinsplit(info, witnesses, anchor); } diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index dcadc6a78..45d9321dd 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -314,7 +314,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf { LOCK(cs_main); consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - anchor = pcoinsTip->GetBestAnchor(); + anchor = pcoinsTip->GetBestAnchor(SPROUT); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 96e85eb19..840b4aaf2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1765,7 +1765,7 @@ void CWallet::WitnessNoteCommitment(std::vector commitments, // Consistency check: we should be able to find the current tree // in our CCoins view. ZCIncrementalMerkleTree dummy_tree; - assert(pcoinsTip->GetAnchorAt(current_anchor, dummy_tree)); + assert(pcoinsTip->GetSproutAnchorAt(current_anchor, dummy_tree)); pindex = chainActive.Next(pindex); } @@ -1819,7 +1819,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ZCIncrementalMerkleTree tree; // This should never fail: we should always be able to get the tree // state on the path to the tip of our chain - assert(pcoinsTip->GetAnchorAt(pindex->hashAnchor, tree)); + assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, tree)); // Increment note witness caches IncrementNoteWitnesses(pindex, &block, tree); diff --git a/src/zcash/IncrementalMerkleTree.cpp b/src/zcash/IncrementalMerkleTree.cpp index f8fafb31a..f8d5fad61 100644 --- a/src/zcash/IncrementalMerkleTree.cpp +++ b/src/zcash/IncrementalMerkleTree.cpp @@ -5,10 +5,41 @@ #include "zcash/IncrementalMerkleTree.hpp" #include "crypto/sha256.h" #include "zcash/util.h" +#include "librustzcash.h" namespace libzcash { -SHA256Compress SHA256Compress::combine(const SHA256Compress& a, const SHA256Compress& b) +PedersenHash PedersenHash::combine( + const PedersenHash& a, + const PedersenHash& b, + size_t depth +) +{ + PedersenHash res = PedersenHash(); + + librustzcash_merkle_hash( + depth, + a.begin(), + b.begin(), + res.begin() + ); + + return res; +} + +PedersenHash PedersenHash::uncommitted() { + PedersenHash res = PedersenHash(); + + librustzcash_tree_uncommitted(res.begin()); + + return res; +} + +SHA256Compress SHA256Compress::combine( + const SHA256Compress& a, + const SHA256Compress& b, + size_t depth +) { SHA256Compress res = SHA256Compress(); @@ -111,7 +142,7 @@ void IncrementalMerkleTree::append(Hash obj) { right = obj; } else { // Combine the leaves and propagate it up the tree - boost::optional combined = Hash::combine(*left, *right); + boost::optional combined = Hash::combine(*left, *right, 0); // Set the "left" leaf to the object and make the "right" leaf none left = obj; @@ -120,7 +151,7 @@ void IncrementalMerkleTree::append(Hash obj) { for (size_t i = 0; i < Depth; i++) { if (i < parents.size()) { if (parents[i]) { - combined = Hash::combine(*parents[i], *combined); + combined = Hash::combine(*parents[i], *combined, i+1); parents[i] = boost::none; } else { parents[i] = *combined; @@ -202,15 +233,15 @@ Hash IncrementalMerkleTree::root(size_t depth, Hash combine_left = left ? *left : filler.next(0); Hash combine_right = right ? *right : filler.next(0); - Hash root = Hash::combine(combine_left, combine_right); + Hash root = Hash::combine(combine_left, combine_right, 0); size_t d = 1; BOOST_FOREACH(const boost::optional& parent, parents) { if (parent) { - root = Hash::combine(*parent, root); + root = Hash::combine(*parent, root, d); } else { - root = Hash::combine(root, filler.next(d)); + root = Hash::combine(root, filler.next(d), d); } d++; @@ -219,7 +250,7 @@ Hash IncrementalMerkleTree::root(size_t depth, // We may not have parents for ancestor trees, so we fill // the rest in here. while (d < depth) { - root = Hash::combine(root, filler.next(d)); + root = Hash::combine(root, filler.next(d), d); d++; } @@ -323,4 +354,10 @@ template class IncrementalMerkleTree; template class IncrementalWitness; +template class IncrementalMerkleTree; +template class IncrementalMerkleTree; + +template class IncrementalWitness; +template class IncrementalWitness; + } // end namespace `libzcash` diff --git a/src/zcash/IncrementalMerkleTree.hpp b/src/zcash/IncrementalMerkleTree.hpp index c4cf9d675..fc476cd29 100644 --- a/src/zcash/IncrementalMerkleTree.hpp +++ b/src/zcash/IncrementalMerkleTree.hpp @@ -57,9 +57,9 @@ template class EmptyMerkleRoots { public: EmptyMerkleRoots() { - empty_roots.at(0) = Hash(); + empty_roots.at(0) = Hash::uncommitted(); for (size_t d = 1; d <= Depth; d++) { - empty_roots.at(d) = Hash::combine(empty_roots.at(d-1), empty_roots.at(d-1)); + empty_roots.at(d) = Hash::combine(empty_roots.at(d-1), empty_roots.at(d-1), d-1); } } Hash empty_root(size_t depth) { @@ -213,7 +213,29 @@ public: SHA256Compress() : uint256() {} SHA256Compress(uint256 contents) : uint256(contents) { } - static SHA256Compress combine(const SHA256Compress& a, const SHA256Compress& b); + static SHA256Compress combine( + const SHA256Compress& a, + const SHA256Compress& b, + size_t depth + ); + + static SHA256Compress uncommitted() { + return SHA256Compress(); + } +}; + +class PedersenHash : public uint256 { +public: + PedersenHash() : uint256() {} + PedersenHash(uint256 contents) : uint256(contents) { } + + static PedersenHash combine( + const PedersenHash& a, + const PedersenHash& b, + size_t depth + ); + + static PedersenHash uncommitted(); }; template @@ -227,4 +249,10 @@ typedef libzcash::IncrementalMerkleTree ZCIncrementalWitness; typedef libzcash::IncrementalWitness ZCTestingIncrementalWitness; +typedef libzcash::IncrementalMerkleTree ZCSaplingIncrementalMerkleTree; +typedef libzcash::IncrementalMerkleTree ZCSaplingTestingIncrementalMerkleTree; + +typedef libzcash::IncrementalWitness ZCSaplingIncrementalWitness; +typedef libzcash::IncrementalWitness ZCSaplingTestingIncrementalWitness; + #endif /* ZC_INCREMENTALMERKLETREE_H_ */ diff --git a/src/zcash/Zcash.h b/src/zcash/Zcash.h index 9e6684475..bb805eef5 100644 --- a/src/zcash/Zcash.h +++ b/src/zcash/Zcash.h @@ -6,6 +6,8 @@ #define INCREMENTAL_MERKLE_TREE_DEPTH 29 #define INCREMENTAL_MERKLE_TREE_DEPTH_TESTING 4 +#define SAPLING_INCREMENTAL_MERKLE_TREE_DEPTH 32 + #define ZC_NOTEPLAINTEXT_LEADING 1 #define ZC_V_SIZE 8 #define ZC_RHO_SIZE 32 diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 53d433c86..80c726b5a 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -366,7 +366,7 @@ public: return false; } - bool GetNullifier(const uint256 &nf, NullifierType type) const { + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -381,7 +381,7 @@ public: bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashAnchor, - CAnchorsMap &mapAnchors, + CAnchorsSproutMap &mapSproutAnchors, CNullifiersMap &mapSproutNullifiers, CNullifiersMap& mapSaplingNullifiers) { return false;