Merge pull request #10448 from ethereum/fixedhash

Add comprehensive tests for FixedHash
This commit is contained in:
Daniel Kirchner 2020-12-11 15:31:20 +01:00 committed by GitHub
commit dc5f7a7405
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 295 additions and 4 deletions

View File

@ -48,6 +48,7 @@ public:
/// The size of the container.
enum { size = N };
static_assert(N != 0);
/// Method to convert from a string.
enum ConstructFromStringType { FromHex, FromBinary };
@ -59,7 +60,13 @@ public:
explicit FixedHash() { m_data.fill(0); }
/// Construct from another hash, filling with zeroes or cropping as necessary.
template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; }
template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft)
{
m_data.fill(0);
unsigned c = std::min(M, N);
for (unsigned i = 0; i < c; ++i)
m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i];
}
/// Convert from the corresponding arithmetic type.
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
@ -104,8 +111,10 @@ public:
}
}
/// Explicitly construct, copying from a string.
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : solidity::util::asBytes(_s), _ht) {}
/// Explicitly construct, copying from a string.
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent):
FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : solidity::util::asBytes(_s), _ht)
{}
/// Convert to arithmetic type.
operator Arith() const { return fromBigEndian<Arith>(m_data); }
@ -114,7 +123,16 @@ public:
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }
bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; }
/// Required to sort objects of this type or use them as map keys.
bool operator<(FixedHash const& _c) const { for (unsigned i = 0; i < N; ++i) if (m_data[i] < _c.m_data[i]) return true; else if (m_data[i] > _c.m_data[i]) return false; return false; }
bool operator<(FixedHash const& _c) const {
for (unsigned i = 0; i < N; ++i)
{
if (m_data[i] < _c.m_data[i])
return true;
else if (m_data[i] > _c.m_data[i])
return false;
}
return false;
}
/// @returns a particular byte from the hash.
uint8_t& operator[](unsigned _i) { return m_data[_i]; }

View File

@ -29,6 +29,7 @@ detect_stray_source_files("${contracts_sources}" "contracts/")
set(libsolutil_sources
libsolutil/Checksum.cpp
libsolutil/CommonData.cpp
libsolutil/FixedHash.cpp
libsolutil/IndentedWriter.cpp
libsolutil/IpfsHash.cpp
libsolutil/IterateReplacing.cpp

View File

@ -0,0 +1,272 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Unit tests for FixedHash.
*/
#include <libsolutil/FixedHash.h>
#include <boost/test/unit_test.hpp>
#include <cstdint>
#include <sstream>
using namespace std;
namespace solidity::util::test
{
static_assert(std::is_same<h160, FixedHash<20>>());
static_assert(std::is_same<h256, FixedHash<32>>());
BOOST_AUTO_TEST_SUITE(FixedHashTest)
BOOST_AUTO_TEST_CASE(default_constructor)
{
BOOST_CHECK_EQUAL(
FixedHash<1>{}.hex(),
"00"
);
BOOST_CHECK_EQUAL(
FixedHash<1>{}.size,
1
);
BOOST_CHECK_EQUAL(
FixedHash<8>{}.hex(),
"0000000000000000"
);
BOOST_CHECK_EQUAL(
FixedHash<8>{}.size,
8
);
BOOST_CHECK_EQUAL(
FixedHash<20>{}.hex(),
"0000000000000000000000000000000000000000"
);
BOOST_CHECK_EQUAL(
FixedHash<20>{}.size,
20
);
BOOST_CHECK_EQUAL(
FixedHash<32>{}.hex(),
"0000000000000000000000000000000000000000000000000000000000000000"
);
BOOST_CHECK_EQUAL(
FixedHash<32>{}.size,
32
);
}
BOOST_AUTO_TEST_CASE(bytes_constructor)
{
FixedHash<8> a(bytes{});
BOOST_CHECK_EQUAL(a.size, 8);
BOOST_CHECK_EQUAL(a.hex(), "0000000000000000");
FixedHash<8> b(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88});
BOOST_CHECK_EQUAL(b.size, 8);
BOOST_CHECK_EQUAL(b.hex(), "1122334455667788");
// TODO: short input, this should fail
FixedHash<8> c(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
BOOST_CHECK_EQUAL(c.size, 8);
BOOST_CHECK_EQUAL(c.hex(), "0000000000000000");
// TODO: oversized input, this should fail
FixedHash<8> d(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99});
BOOST_CHECK_EQUAL(d.size, 8);
BOOST_CHECK_EQUAL(d.hex(), "0000000000000000");
}
// TODO: add FixedHash(bytesConstRef) constructor
BOOST_AUTO_TEST_CASE(string_constructor_fromhex)
{
// TODO: this tests the default settings ConstructFromStringType::fromHex, ConstructFromHashType::FailIfDifferent
// should test other options too
FixedHash<8> a("");
BOOST_CHECK_EQUAL(a.size, 8);
BOOST_CHECK_EQUAL(a.hex(), "0000000000000000");
FixedHash<8> b("1122334455667788");
BOOST_CHECK_EQUAL(b.size, 8);
BOOST_CHECK_EQUAL(b.hex(), "1122334455667788");
FixedHash<8> c("0x1122334455667788");
BOOST_CHECK_EQUAL(c.size, 8);
BOOST_CHECK_EQUAL(c.hex(), "1122334455667788");
// TODO: short input, this should fail
FixedHash<8> d("112233445566");
BOOST_CHECK_EQUAL(d.size, 8);
BOOST_CHECK_EQUAL(d.hex(), "0000000000000000");
// TODO: oversized input, this should fail
FixedHash<8> e("112233445566778899");
BOOST_CHECK_EQUAL(e.size, 8);
BOOST_CHECK_EQUAL(e.hex(), "0000000000000000");
}
BOOST_AUTO_TEST_CASE(string_constructor_frombytes)
{
FixedHash<8> b("", FixedHash<8>::FromBinary);
BOOST_CHECK_EQUAL(b.size, 8);
BOOST_CHECK_EQUAL(b.hex(), "0000000000000000");
FixedHash<8> c("abcdefgh", FixedHash<8>::FromBinary);
BOOST_CHECK_EQUAL(c.size, 8);
BOOST_CHECK_EQUAL(c.hex(), "6162636465666768");
// TODO: short input, this should fail
FixedHash<8> d("abcdefg", FixedHash<8>::FromBinary);
BOOST_CHECK_EQUAL(d.size, 8);
BOOST_CHECK_EQUAL(d.hex(), "0000000000000000");
// TODO: oversized input, this should fail
FixedHash<8> e("abcdefghi", FixedHash<8>::FromBinary);
BOOST_CHECK_EQUAL(e.size, 8);
BOOST_CHECK_EQUAL(e.hex(), "0000000000000000");
}
BOOST_AUTO_TEST_CASE(converting_constructor)
{
// Truncation
FixedHash<8> a = FixedHash<8>(FixedHash<12>("112233445566778899001122"));
BOOST_CHECK_EQUAL(a.size, 8);
BOOST_CHECK_EQUAL(a.hex(), "1122334455667788");
// Left-aligned extension
FixedHash<12> b = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::AlignLeft);
BOOST_CHECK_EQUAL(b.size, 12);
BOOST_CHECK_EQUAL(b.hex(), "112233445566778800000000");
// Right-aligned extension
FixedHash<12> c = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::AlignRight);
BOOST_CHECK_EQUAL(c.size, 12);
BOOST_CHECK_EQUAL(c.hex(), "000000001122334455667788");
// Default setting
FixedHash<12> d = FixedHash<12>(FixedHash<8>("1122334455667788"));
BOOST_CHECK_EQUAL(d, b);
// FailIfDifferent setting
// TODO: Shouldn't this throw?
FixedHash<12> e = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::FailIfDifferent);
BOOST_CHECK_EQUAL(e, b);
}
BOOST_AUTO_TEST_CASE(arith_constructor)
{
FixedHash<20> a(u160(0x1234));
BOOST_CHECK_EQUAL(
a.hex(),
"0000000000000000000000000000000000001234"
);
FixedHash<32> b(u256(0x12340000));
BOOST_CHECK_EQUAL(
b.hex(),
"0000000000000000000000000000000000000000000000000000000012340000"
);
// NOTE: size-mismatched constructor is not available
}
BOOST_AUTO_TEST_CASE(to_arith)
{
FixedHash<20> a{};
BOOST_CHECK_EQUAL(u160(a), 0);
FixedHash<32> b("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
BOOST_CHECK_EQUAL(u256(b), u256("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"));
}
BOOST_AUTO_TEST_CASE(comparison)
{
FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
FixedHash<32> b("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
FixedHash<32> c("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a471");
FixedHash<32> d("233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470c5d2460186f7");
BOOST_CHECK(a == a);
BOOST_CHECK(b == b);
BOOST_CHECK(a == b);
BOOST_CHECK(b == a);
BOOST_CHECK(a != c);
BOOST_CHECK(c != a);
BOOST_CHECK(a != d);
BOOST_CHECK(d != a);
// Only equal size comparison is supported.
BOOST_CHECK(FixedHash<8>{} == FixedHash<8>{});
BOOST_CHECK(FixedHash<32>{} != b);
// Only equal size less than comparison is supported.
BOOST_CHECK(!(a < b));
BOOST_CHECK(d < c);
BOOST_CHECK(FixedHash<32>{} < a);
}
BOOST_AUTO_TEST_CASE(indexing)
{
// NOTE: uses std::array, so "Accessing a nonexistent element through this operator is undefined behavior."
FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
BOOST_CHECK_EQUAL(a[0], 0xc5);
BOOST_CHECK_EQUAL(a[1], 0xd2);
BOOST_CHECK_EQUAL(a[31], 0x70);
a[0] = 0xff;
a[31] = 0x54;
BOOST_CHECK_EQUAL(a[0], 0xff);
BOOST_CHECK_EQUAL(a[1], 0xd2);
BOOST_CHECK_EQUAL(a[31], 0x54);
}
BOOST_AUTO_TEST_CASE(misc)
{
FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
uint8_t* mut_a = a.data();
uint8_t const* const_a = a.data();
BOOST_CHECK_EQUAL(mut_a, const_a);
BOOST_CHECK_EQUAL(memcmp(mut_a, const_a, a.size), 0);
bytes bytes_a = a.asBytes();
bytesRef bytesref_a = a.ref();
bytesConstRef bytesconstref_a = a.ref();
// There's no printing for bytesRef/bytesConstRef
BOOST_CHECK(bytes(a.data(), a.data() + a.size) == bytes_a);
BOOST_CHECK(bytesRef(a.data(), a.size) == bytesref_a);
BOOST_CHECK(bytesConstRef(a.data(), a.size) == bytesconstref_a);
}
BOOST_AUTO_TEST_CASE(tostream)
{
std::ostringstream out;
out << FixedHash<4>("44114411");
out << FixedHash<32>{};
out << FixedHash<2>("f77f");
out << FixedHash<1>("1");
BOOST_CHECK_EQUAL(out.str(), "441144110000000000000000000000000000000000000000000000000000000000000000f77f01");
}
BOOST_AUTO_TEST_SUITE_END()
}