LCOV - code coverage report
Current view: top level - src/jamidht/eth/libdevcore - FixedHash.h (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 36 82 43.9 %
Date: 2021-07-24 07:34:39 Functions: 25 40 62.5 %

          Line data    Source code
       1             : /*
       2             :     This file is part of cpp-ethereum.
       3             : 
       4             :     cpp-ethereum is free software: you can redistribute it and/or modify
       5             :     it under the terms of the GNU General Public License as published by
       6             :     the Free Software Foundation, either version 3 of the License, or
       7             :     (at your option) any later version.
       8             : 
       9             :     cpp-ethereum is distributed in the hope that it will be useful,
      10             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :     GNU General Public License for more details.
      13             : 
      14             :     You should have received a copy of the GNU General Public License
      15             :     along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
      16             : */
      17             : /** @file FixedHash.h
      18             :  * @author Gav Wood <i@gavwood.com>
      19             :  * @date 2014
      20             :  *
      21             :  * The FixedHash fixed-size "hash" container type.
      22             :  */
      23             : 
      24             : #pragma once
      25             : 
      26             : #include <array>
      27             : #include <cstdint>
      28             : #include <algorithm>
      29             : #include <random>
      30             : #include <sstream>
      31             : #include <opendht/rng.h>
      32             : 
      33             : #include "CommonData.h"
      34             : 
      35             : namespace dev {
      36             : 
      37             : /// Compile-time calculation of Log2 of constant values.
      38             : template<unsigned N>
      39             : struct StaticLog2
      40             : {
      41             :     enum { result = 1 + StaticLog2<N / 2>::result };
      42             : };
      43             : template<>
      44             : struct StaticLog2<1>
      45             : {
      46             :     enum { result = 0 };
      47             : };
      48             : 
      49             : extern dht::crypto::random_device s_fixedHashEngine;
      50             : 
      51             : /// Fixed-size raw-byte array container type, with an API optimised for storing hashes.
      52             : /// Transparently converts to/from the corresponding arithmetic type; this will
      53             : /// assume the data contained in the hash is big-endian.
      54             : template<unsigned N>
      55             : class FixedHash
      56             : {
      57             : public:
      58             :     /// The size of the container.
      59             :     enum { size = N };
      60             : 
      61             :     /// A dummy flag to avoid accidental construction from pointer.
      62             :     enum ConstructFromPointerType { ConstructFromPointer };
      63             : 
      64             :     /// Method to convert from a string.
      65             :     enum ConstructFromStringType { FromHex, FromBinary };
      66             : 
      67             :     /// Method to convert from a string.
      68             :     enum ConstructFromHashType { AlignLeft, AlignRight, FailIfDifferent };
      69             : 
      70             :     /// Construct an empty hash.
      71        2200 :     FixedHash() { m_data.fill(0); }
      72             : 
      73             :     /// Construct from another hash, filling with zeroes or cropping as necessary.
      74             :     template<unsigned M>
      75             :     explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft)
      76             :     {
      77             :         m_data.fill(0);
      78             :         unsigned c = std::min(M, N);
      79             :         for (unsigned i = 0; i < c; ++i)
      80             :             m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i];
      81             :     }
      82             : 
      83             :     /// Convert from unsigned
      84             :     explicit FixedHash(unsigned _u) { toBigEndian(_u, m_data); }
      85             : 
      86             :     /// Explicitly construct, copying from a byte array.
      87         318 :     explicit FixedHash(bytes const& _b, ConstructFromHashType _t = FailIfDifferent)
      88             :     {
      89         318 :         if (_b.size() == N)
      90         318 :             memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N));
      91             :         else {
      92           0 :             m_data.fill(0);
      93           0 :             if (_t != FailIfDifferent) {
      94           0 :                 auto c = std::min<unsigned>(_b.size(), N);
      95           0 :                 for (unsigned i = 0; i < c; ++i)
      96           0 :                     m_data[_t == AlignRight ? N - 1 - i : i]
      97           0 :                         = _b[_t == AlignRight ? _b.size() - 1 - i : i];
      98             :             }
      99             :         }
     100         318 :     }
     101             : 
     102             :     /// Explicitly construct, copying from a byte array.
     103             :     explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent)
     104             :     {
     105             :         if (_b.size() == N)
     106             :             memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N));
     107             :         else {
     108             :             m_data.fill(0);
     109             :             if (_t != FailIfDifferent) {
     110             :                 auto c = std::min<unsigned>(_b.size(), N);
     111             :                 for (unsigned i = 0; i < c; ++i)
     112             :                     m_data[_t == AlignRight ? N - 1 - i : i]
     113             :                         = _b[_t == AlignRight ? _b.size() - 1 - i : i];
     114             :             }
     115             :         }
     116             :     }
     117             : 
     118             :     /// Explicitly construct, copying from a bytes in memory with given pointer.
     119         622 :     explicit FixedHash(uint8_t const* _bs, ConstructFromPointerType)
     120             :     {
     121         622 :         memcpy(m_data.data(), _bs, N);
     122         622 :     }
     123             : 
     124             :     /// Explicitly construct, copying from a  string.
     125           0 :     explicit FixedHash(std::string const& _s,
     126             :                        ConstructFromStringType _t = FromHex,
     127             :                        ConstructFromHashType _ht = FailIfDifferent)
     128           0 :         : FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : dev::asBytes(_s), _ht)
     129           0 :     {}
     130             : 
     131             :     /// @returns true iff this is the empty hash.
     132         926 :     explicit operator bool() const
     133             :     {
     134        1853 :         return std::any_of(m_data.begin(), m_data.end(), [](uint8_t _b) { return _b != 0; });
     135             :     }
     136             : 
     137             :     // The obvious comparison operators.
     138             :     bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }
     139             :     bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; }
     140           0 :     bool operator<(FixedHash const& _c) const
     141             :     {
     142           0 :         for (unsigned i = 0; i < N; ++i)
     143           0 :             if (m_data[i] < _c.m_data[i])
     144           0 :                 return true;
     145           0 :             else if (m_data[i] > _c.m_data[i])
     146           0 :                 return false;
     147           0 :         return false;
     148             :     }
     149             :     bool operator>=(FixedHash const& _c) const { return !operator<(_c); }
     150           0 :     bool operator<=(FixedHash const& _c) const { return operator==(_c) || operator<(_c); }
     151           0 :     bool operator>(FixedHash const& _c) const { return !operator<=(_c); }
     152             : 
     153             :     // The obvious binary operators.
     154           0 :     FixedHash& operator^=(FixedHash const& _c)
     155             :     {
     156           0 :         for (unsigned i = 0; i < N; ++i)
     157           0 :             m_data[i] ^= _c.m_data[i];
     158           0 :         return *this;
     159             :     }
     160             :     FixedHash operator^(FixedHash const& _c) const { return FixedHash(*this) ^= _c; }
     161             :     FixedHash& operator|=(FixedHash const& _c)
     162             :     {
     163             :         for (unsigned i = 0; i < N; ++i)
     164             :             m_data[i] |= _c.m_data[i];
     165             :         return *this;
     166             :     }
     167             :     FixedHash operator|(FixedHash const& _c) const { return FixedHash(*this) |= _c; }
     168             :     FixedHash& operator&=(FixedHash const& _c)
     169             :     {
     170             :         for (unsigned i = 0; i < N; ++i)
     171             :             m_data[i] &= _c.m_data[i];
     172             :         return *this;
     173             :     }
     174             :     FixedHash operator&(FixedHash const& _c) const { return FixedHash(*this) &= _c; }
     175           0 :     FixedHash operator~() const
     176             :     {
     177           0 :         FixedHash ret;
     178           0 :         for (unsigned i = 0; i < N; ++i)
     179           0 :             ret[i] = ~m_data[i];
     180           0 :         return ret;
     181             :     }
     182             : 
     183             :     // Big-endian increment.
     184             :     FixedHash& operator++()
     185             :     {
     186             :         for (unsigned i = size; i > 0 && !++m_data[--i];) {
     187             :         }
     188             :         return *this;
     189             :     }
     190             : 
     191             :     /// @returns true if all one-bits in @a _c are set in this object.
     192             :     bool contains(FixedHash const& _c) const { return (*this & _c) == _c; }
     193             : 
     194             :     /// @returns a particular byte from the hash.
     195           0 :     uint8_t& operator[](unsigned _i) { return m_data[_i]; }
     196             :     /// @returns a particular byte from the hash.
     197             :     uint8_t operator[](unsigned _i) const { return m_data[_i]; }
     198             : 
     199             :     /// @returns an abridged version of the hash as a user-readable hex string.
     200             :     std::string abridged() const { return toHex(ref().cropped(0, 4)) + "\342\200\246"; }
     201             : 
     202             :     /// @returns a version of the hash as a user-readable hex string that leaves out the middle part.
     203             :     std::string abridgedMiddle() const
     204             :     {
     205             :         return toHex(ref().cropped(0, 4)) + "\342\200\246" + toHex(ref().cropped(N - 4));
     206             :     }
     207             : 
     208             :     /// @returns the hash as a user-readable hex string.
     209         636 :     std::string hex() const { return toHex(ref()); }
     210             : 
     211             :     /// @returns a mutable byte vector_ref to the object's data.
     212         652 :     bytesRef ref() { return bytesRef(m_data.data(), N); }
     213             : 
     214             :     /// @returns a constant byte vector_ref to the object's data.
     215        3096 :     bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); }
     216             : 
     217             :     /// @returns a mutable byte pointer to the object's data.
     218         622 :     uint8_t* data() { return m_data.data(); }
     219             : 
     220             :     /// @returns a constant byte pointer to the object's data.
     221        1852 :     uint8_t const* data() const { return m_data.data(); }
     222             : 
     223             :     /// @returns begin iterator.
     224             :     auto begin() const -> typename std::array<uint8_t, N>::const_iterator { return m_data.begin(); }
     225             : 
     226             :     /// @returns end iterator.
     227             :     auto end() const -> typename std::array<uint8_t, N>::const_iterator { return m_data.end(); }
     228             : 
     229             :     /// @returns a copy of the object's data as a byte vector.
     230         304 :     bytes asBytes() const { return bytes(data(), data() + N); }
     231             : 
     232             :     /// @returns a mutable reference to the object's data as an STL array.
     233             :     std::array<uint8_t, N>& asArray() { return m_data; }
     234             : 
     235             :     /// @returns a constant reference to the object's data as an STL array.
     236             :     std::array<uint8_t, N> const& asArray() const { return m_data; }
     237             : 
     238             :     /// Populate with random data.
     239             :     template<class Engine>
     240         304 :     void randomize(Engine& _eng)
     241             :     {
     242       10032 :         for (auto& i : m_data)
     243        9728 :             i = (uint8_t) std::uniform_int_distribution<uint16_t>(0, 255)(_eng);
     244         304 :     }
     245             : 
     246             :     /// @returns a random valued object.
     247             :     static FixedHash random()
     248             :     {
     249             :         FixedHash ret;
     250             :         ret.randomize(s_fixedHashEngine);
     251             :         return ret;
     252             :     }
     253             : 
     254             :     template<unsigned P, unsigned M>
     255             :     inline FixedHash& shiftBloom(FixedHash<M> const& _h)
     256             :     {
     257             :         return (*this |= _h.template bloomPart<P, N>());
     258             :     }
     259             : 
     260             :     template<unsigned P, unsigned M>
     261             :     inline bool containsBloom(FixedHash<M> const& _h)
     262             :     {
     263             :         return contains(_h.template bloomPart<P, N>());
     264             :     }
     265             : 
     266             :     template<unsigned P, unsigned M>
     267             :     inline FixedHash<M> bloomPart() const
     268             :     {
     269             :         unsigned const c_bloomBits = M * 8;
     270             :         unsigned const c_mask = c_bloomBits - 1;
     271             :         unsigned const c_bloomBytes = (StaticLog2<c_bloomBits>::result + 7) / 8;
     272             : 
     273             :         static_assert((M & (M - 1)) == 0, "M must be power-of-two");
     274             :         static_assert(P * c_bloomBytes <= N, "out of range");
     275             : 
     276             :         FixedHash<M> ret;
     277             :         uint8_t const* p = data();
     278             :         for (unsigned i = 0; i < P; ++i) {
     279             :             unsigned index = 0;
     280             :             for (unsigned j = 0; j < c_bloomBytes; ++j, ++p)
     281             :                 index = (index << 8) | *p;
     282             :             index &= c_mask;
     283             :             ret[M - 1 - index / 8] |= (1 << (index % 8));
     284             :         }
     285             :         return ret;
     286             :     }
     287             : 
     288             :     /// Returns the index of the first bit set to one, or size() * 8 if no bits are set.
     289             :     inline unsigned firstBitSet() const
     290             :     {
     291             :         unsigned ret = 0;
     292             :         for (auto d : m_data)
     293             :             if (d)
     294             :                 for (;; ++ret, d <<= 1)
     295             :                     if (d & 0x80)
     296             :                         return ret;
     297             :                     else {
     298             :                     }
     299             :             else
     300             :                 ret += 8;
     301             :         return ret;
     302             :     }
     303             : 
     304             :     void clear() { m_data.fill(0); }
     305             : 
     306             : private:
     307             :     std::array<uint8_t, N> m_data; ///< The binary data.
     308             : };
     309             : 
     310             : template<unsigned T>
     311             : class SecureFixedHash : private FixedHash<T>
     312             : {
     313             : public:
     314             :     using ConstructFromHashType = typename FixedHash<T>::ConstructFromHashType;
     315             :     using ConstructFromStringType = typename FixedHash<T>::ConstructFromStringType;
     316             :     using ConstructFromPointerType = typename FixedHash<T>::ConstructFromPointerType;
     317         304 :     SecureFixedHash() = default;
     318         318 :     explicit SecureFixedHash(bytes const& _b,
     319             :                              ConstructFromHashType _t = FixedHash<T>::FailIfDifferent)
     320         318 :         : FixedHash<T>(_b, _t)
     321         318 :     {}
     322             :     explicit SecureFixedHash(bytesConstRef _b,
     323             :                              ConstructFromHashType _t = FixedHash<T>::FailIfDifferent)
     324             :         : FixedHash<T>(_b, _t)
     325             :     {}
     326             :     explicit SecureFixedHash(bytesSec const& _b,
     327             :                              ConstructFromHashType _t = FixedHash<T>::FailIfDifferent)
     328             :         : FixedHash<T>(_b.ref(), _t)
     329             :     {}
     330             :     template<unsigned M>
     331             :     explicit SecureFixedHash(FixedHash<M> const& _h,
     332             :                              ConstructFromHashType _t = FixedHash<T>::AlignLeft)
     333             :         : FixedHash<T>(_h, _t)
     334             :     {}
     335             :     template<unsigned M>
     336             :     explicit SecureFixedHash(SecureFixedHash<M> const& _h,
     337             :                              ConstructFromHashType _t = FixedHash<T>::AlignLeft)
     338             :         : FixedHash<T>(_h.makeInsecure(), _t)
     339             :     {}
     340             :     explicit SecureFixedHash(std::string const& _s,
     341             :                              ConstructFromStringType _t = FixedHash<T>::FromHex,
     342             :                              ConstructFromHashType _ht = FixedHash<T>::FailIfDifferent)
     343             :         : FixedHash<T>(_s, _t, _ht)
     344             :     {}
     345             :     explicit SecureFixedHash(bytes const* _d, ConstructFromPointerType _t)
     346             :         : FixedHash<T>(_d, _t)
     347             :     {}
     348        2156 :     ~SecureFixedHash() { ref().cleanse(); }
     349             : 
     350           0 :     SecureFixedHash<T>& operator=(SecureFixedHash<T> const& _c)
     351             :     {
     352           0 :         if (&_c == this)
     353           0 :             return *this;
     354           0 :         ref().cleanse();
     355           0 :         FixedHash<T>::operator=(static_cast<FixedHash<T> const&>(_c));
     356           0 :         return *this;
     357             :     }
     358             : 
     359             :     using FixedHash<T>::size;
     360             : 
     361             :     bytesSec asBytesSec() const { return bytesSec(ref()); }
     362             : 
     363         304 :     FixedHash<T> const& makeInsecure() const { return static_cast<FixedHash<T> const&>(*this); }
     364           0 :     FixedHash<T>& writable()
     365             :     {
     366           0 :         clear();
     367           0 :         return static_cast<FixedHash<T>&>(*this);
     368             :     }
     369             : 
     370             :     using FixedHash<T>::operator bool;
     371             : 
     372             :     // The obvious comparison operators.
     373             :     bool operator==(SecureFixedHash const& _c) const
     374             :     {
     375             :         return static_cast<FixedHash<T> const&>(*this).operator==(
     376             :             static_cast<FixedHash<T> const&>(_c));
     377             :     }
     378             :     bool operator!=(SecureFixedHash const& _c) const
     379             :     {
     380             :         return static_cast<FixedHash<T> const&>(*this).operator!=(
     381             :             static_cast<FixedHash<T> const&>(_c));
     382             :     }
     383             :     bool operator<(SecureFixedHash const& _c) const
     384             :     {
     385             :         return static_cast<FixedHash<T> const&>(*this).operator<(
     386             :             static_cast<FixedHash<T> const&>(_c));
     387             :     }
     388             :     bool operator>=(SecureFixedHash const& _c) const
     389             :     {
     390             :         return static_cast<FixedHash<T> const&>(*this).operator>=(
     391             :             static_cast<FixedHash<T> const&>(_c));
     392             :     }
     393             :     bool operator<=(SecureFixedHash const& _c) const
     394             :     {
     395             :         return static_cast<FixedHash<T> const&>(*this).operator<=(
     396             :             static_cast<FixedHash<T> const&>(_c));
     397             :     }
     398             :     bool operator>(SecureFixedHash const& _c) const
     399             :     {
     400             :         return static_cast<FixedHash<T> const&>(*this).operator>(
     401             :             static_cast<FixedHash<T> const&>(_c));
     402             :     }
     403             : 
     404             :     using FixedHash<T>::operator==;
     405             :     using FixedHash<T>::operator!=;
     406             :     using FixedHash<T>::operator<;
     407             :     using FixedHash<T>::operator>=;
     408             :     using FixedHash<T>::operator<=;
     409             :     using FixedHash<T>::operator>;
     410             : 
     411             :     // The obvious binary operators.
     412             :     SecureFixedHash& operator^=(FixedHash<T> const& _c)
     413             :     {
     414             :         static_cast<FixedHash<T>&>(*this).operator^=(_c);
     415             :         return *this;
     416             :     }
     417             :     SecureFixedHash operator^(FixedHash<T> const& _c) const { return SecureFixedHash(*this) ^= _c; }
     418             :     SecureFixedHash& operator|=(FixedHash<T> const& _c)
     419             :     {
     420             :         static_cast<FixedHash<T>&>(*this).operator^=(_c);
     421             :         return *this;
     422             :     }
     423             :     SecureFixedHash operator|(FixedHash<T> const& _c) const { return SecureFixedHash(*this) |= _c; }
     424             :     SecureFixedHash& operator&=(FixedHash<T> const& _c)
     425             :     {
     426             :         static_cast<FixedHash<T>&>(*this).operator^=(_c);
     427             :         return *this;
     428             :     }
     429             :     SecureFixedHash operator&(FixedHash<T> const& _c) const { return SecureFixedHash(*this) &= _c; }
     430             : 
     431             :     SecureFixedHash& operator^=(SecureFixedHash const& _c)
     432             :     {
     433             :         static_cast<FixedHash<T>&>(*this).operator^=(static_cast<FixedHash<T> const&>(_c));
     434             :         return *this;
     435             :     }
     436             :     SecureFixedHash operator^(SecureFixedHash const& _c) const
     437             :     {
     438             :         return SecureFixedHash(*this) ^= _c;
     439             :     }
     440             :     SecureFixedHash& operator|=(SecureFixedHash const& _c)
     441             :     {
     442             :         static_cast<FixedHash<T>&>(*this).operator^=(static_cast<FixedHash<T> const&>(_c));
     443             :         return *this;
     444             :     }
     445             :     SecureFixedHash operator|(SecureFixedHash const& _c) const
     446             :     {
     447             :         return SecureFixedHash(*this) |= _c;
     448             :     }
     449             :     SecureFixedHash& operator&=(SecureFixedHash const& _c)
     450             :     {
     451             :         static_cast<FixedHash<T>&>(*this).operator^=(static_cast<FixedHash<T> const&>(_c));
     452             :         return *this;
     453             :     }
     454             :     SecureFixedHash operator&(SecureFixedHash const& _c) const
     455             :     {
     456             :         return SecureFixedHash(*this) &= _c;
     457             :     }
     458           0 :     SecureFixedHash operator~() const
     459             :     {
     460           0 :         auto r = ~static_cast<FixedHash<T> const&>(*this);
     461           0 :         return static_cast<SecureFixedHash const&>(r);
     462             :     }
     463             : 
     464             :     using FixedHash<T>::abridged;
     465             :     using FixedHash<T>::abridgedMiddle;
     466             : 
     467        2156 :     bytesConstRef ref() const { return FixedHash<T>::ref(); }
     468         622 :     uint8_t const* data() const { return FixedHash<T>::data(); }
     469             : 
     470         304 :     static SecureFixedHash<T> random()
     471             :     {
     472         304 :         SecureFixedHash<T> ret;
     473         304 :         ret.randomize(s_fixedHashEngine);
     474         304 :         return ret;
     475             :     }
     476             :     using FixedHash<T>::firstBitSet;
     477             : 
     478           0 :     void clear() { ref().cleanse(); }
     479             : };
     480             : 
     481             : /// Fast equality operator for h256.
     482             : template<>
     483             : inline bool
     484           0 : FixedHash<32>::operator==(FixedHash<32> const& _other) const
     485             : {
     486           0 :     const uint64_t* hash1 = (const uint64_t*) data();
     487           0 :     const uint64_t* hash2 = (const uint64_t*) _other.data();
     488           0 :     return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2])
     489           0 :            && (hash1[3] == hash2[3]);
     490             : }
     491             : 
     492             : /// Stream I/O for the FixedHash class.
     493             : template<unsigned N>
     494             : inline std::ostream&
     495             : operator<<(std::ostream& _out, FixedHash<N> const& _h)
     496             : {
     497             :     _out << toHex(_h);
     498             :     return _out;
     499             : }
     500             : 
     501             : /// Stream I/O for the SecureFixedHash class.
     502             : template<unsigned N>
     503             : inline std::ostream&
     504             : operator<<(std::ostream& _out, SecureFixedHash<N> const& _h)
     505             : {
     506             :     _out << "SecureFixedHash#" << std::hex << typename FixedHash<N>::hash()(_h.makeInsecure())
     507             :          << std::dec;
     508             :     return _out;
     509             : }
     510             : 
     511             : // Common types of FixedHash.
     512             : using h2048 = FixedHash<256>;
     513             : using h1024 = FixedHash<128>;
     514             : using h520 = FixedHash<65>;
     515             : using h512 = FixedHash<64>;
     516             : using h256 = FixedHash<32>;
     517             : using h160 = FixedHash<20>;
     518             : using h128 = FixedHash<16>;
     519             : using h64 = FixedHash<8>;
     520             : using h512s = std::vector<h512>;
     521             : using h256s = std::vector<h256>;
     522             : using h160s = std::vector<h160>;
     523             : using h256Set = std::set<h256>;
     524             : using h160Set = std::set<h160>;
     525             : using h256Hash = std::unordered_set<h256>;
     526             : using h160Hash = std::unordered_set<h160>;
     527             : 
     528             : /// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes.
     529             : inline h160
     530         622 : right160(h256 const& _t)
     531             : {
     532         622 :     h160 ret;
     533         622 :     memcpy(ret.data(), _t.data() + 12, 20);
     534         622 :     return ret;
     535             : }
     536             : 
     537             : inline std::string
     538             : toString(h256s const& _bs)
     539             : {
     540             :     std::ostringstream out;
     541             :     out << "[ ";
     542             :     for (h256 const& i : _bs)
     543             :         out << i.abridged() << ", ";
     544             :     out << "]";
     545             :     return out.str();
     546             : }
     547             : 
     548             : } // namespace dev

Generated by: LCOV version 1.14