dncurrency/nano/core_test/signing.cpp

201 lines
7.4 KiB
C++

#include <nano/node/signatures.hpp>
#include <nano/secure/common.hpp>
#include <gtest/gtest.h>
TEST (signature_checker, empty)
{
nano::signature_checker checker (0);
nano::signature_check_set check = { 0, nullptr, nullptr, nullptr, nullptr, nullptr };
checker.verify (check);
}
TEST (signature_checker, bulk_single_thread)
{
nano::keypair key;
nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0);
nano::signature_checker checker (0);
std::vector<nano::uint256_union> hashes;
size_t size (1000);
hashes.reserve (size);
std::vector<unsigned char const *> messages;
messages.reserve (size);
std::vector<size_t> lengths;
lengths.reserve (size);
std::vector<unsigned char const *> pub_keys;
pub_keys.reserve (size);
std::vector<unsigned char const *> signatures;
signatures.reserve (size);
std::vector<int> verifications;
verifications.resize (size);
for (auto i (0); i < size; ++i)
{
hashes.push_back (block.hash ());
messages.push_back (hashes.back ().bytes.data ());
lengths.push_back (sizeof (decltype (hashes)::value_type));
pub_keys.push_back (block.hashables.account.bytes.data ());
signatures.push_back (block.signature.bytes.data ());
}
nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () };
checker.verify (check);
bool all_valid = std::all_of (verifications.cbegin (), verifications.cend (), [] (auto verification) { return verification == 1; });
ASSERT_TRUE (all_valid);
}
TEST (signature_checker, many_multi_threaded)
{
nano::signature_checker checker (4);
auto signature_checker_work_func = [&checker] () {
nano::keypair key;
nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0);
auto block_hash = block.hash ();
nano::state_block invalid_block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0);
invalid_block.signature.bytes[31] ^= 0x1;
auto invalid_block_hash = block.hash ();
constexpr auto num_check_sizes = 18;
constexpr std::array<size_t, num_check_sizes> check_sizes{ 2048, 256, 1024, 1,
4096, 512, 2050, 1024, 8092, 513, 17, 1024, 2047, 255, 513, 2049, 1025, 1023 };
std::vector<nano::signature_check_set> signature_checker_sets;
signature_checker_sets.reserve (num_check_sizes);
// Create containers so everything is kept in scope while the threads work on the signature checks
std::array<std::vector<unsigned char const *>, num_check_sizes> messages;
std::array<std::vector<size_t>, num_check_sizes> lengths;
std::array<std::vector<unsigned char const *>, num_check_sizes> pub_keys;
std::array<std::vector<unsigned char const *>, num_check_sizes> signatures;
std::array<std::vector<int>, num_check_sizes> verifications;
// Populate all the signature check sets. The last one in each set is given an incorrect block signature.
for (int i = 0; i < num_check_sizes; ++i)
{
auto check_size = check_sizes[i];
ASSERT_GT (check_size, 0);
auto last_signature_index = check_size - 1;
messages[i].resize (check_size);
std::fill (messages[i].begin (), messages[i].end (), block_hash.bytes.data ());
messages[i][last_signature_index] = invalid_block_hash.bytes.data ();
lengths[i].resize (check_size);
std::fill (lengths[i].begin (), lengths[i].end (), sizeof (decltype (block_hash)));
pub_keys[i].resize (check_size);
std::fill (pub_keys[i].begin (), pub_keys[i].end (), block.hashables.account.bytes.data ());
pub_keys[i][last_signature_index] = invalid_block.hashables.account.bytes.data ();
signatures[i].resize (check_size);
std::fill (signatures[i].begin (), signatures[i].end (), block.signature.bytes.data ());
signatures[i][last_signature_index] = invalid_block.signature.bytes.data ();
verifications[i].resize (check_size);
signature_checker_sets.emplace_back (check_size, messages[i].data (), lengths[i].data (), pub_keys[i].data (), signatures[i].data (), verifications[i].data ());
checker.verify (signature_checker_sets[i]);
// Confirm all but last are valid
auto all_valid = std::all_of (verifications[i].cbegin (), verifications[i].cend () - 1, [] (auto verification) { return verification == 1; });
ASSERT_TRUE (all_valid);
ASSERT_EQ (verifications[i][last_signature_index], 0);
}
};
std::thread signature_checker_thread1 (signature_checker_work_func);
std::thread signature_checker_thread2 (signature_checker_work_func);
signature_checker_thread1.join ();
signature_checker_thread2.join ();
}
TEST (signature_checker, one)
{
nano::signature_checker checker (0);
auto verify_block = [&checker] (auto & block, auto result) {
std::vector<nano::uint256_union> hashes;
std::vector<unsigned char const *> messages;
std::vector<size_t> lengths;
std::vector<unsigned char const *> pub_keys;
std::vector<unsigned char const *> signatures;
std::vector<int> verifications;
size_t size (1);
verifications.resize (size);
for (auto i (0); i < size; ++i)
{
hashes.push_back (block.hash ());
messages.push_back (hashes.back ().bytes.data ());
lengths.push_back (sizeof (decltype (hashes)::value_type));
pub_keys.push_back (block.hashables.account.bytes.data ());
signatures.push_back (block.signature.bytes.data ());
}
nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () };
checker.verify (check);
ASSERT_EQ (verifications.front (), result);
};
nano::keypair key;
nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0);
// Make signaure invalid and check result is incorrect
block.signature.bytes[31] ^= 0x1;
verify_block (block, 0);
// Make it valid and check for succcess
block.signature.bytes[31] ^= 0x1;
verify_block (block, 1);
}
TEST (signature_checker, boundary_checks)
{
// sizes container must be in incrementing order
std::vector<size_t> sizes{ 0, 1 };
auto add_boundary = [&sizes] (size_t boundary) {
sizes.insert (sizes.end (), { boundary - 1, boundary, boundary + 1 });
};
for (auto i = 1; i <= 5; ++i)
{
add_boundary (nano::signature_checker::batch_size * i);
}
nano::signature_checker checker (1);
auto max_size = *(sizes.end () - 1);
std::vector<nano::uint256_union> hashes;
hashes.reserve (max_size);
std::vector<unsigned char const *> messages;
messages.reserve (max_size);
std::vector<size_t> lengths;
lengths.reserve (max_size);
std::vector<unsigned char const *> pub_keys;
pub_keys.reserve (max_size);
std::vector<unsigned char const *> signatures;
signatures.reserve (max_size);
nano::keypair key;
nano::state_block block (key.pub, 0, key.pub, 0, 0, key.prv, key.pub, 0);
size_t last_size = 0;
for (auto size : sizes)
{
// The size needed to append to existing containers, saves re-initializing from scratch each iteration
auto extra_size = size - last_size;
std::vector<int> verifications;
verifications.resize (size);
for (auto i (0); i < extra_size; ++i)
{
hashes.push_back (block.hash ());
messages.push_back (hashes.back ().bytes.data ());
lengths.push_back (sizeof (decltype (hashes)::value_type));
pub_keys.push_back (block.hashables.account.bytes.data ());
signatures.push_back (block.signature.bytes.data ());
}
nano::signature_check_set check = { size, messages.data (), lengths.data (), pub_keys.data (), signatures.data (), verifications.data () };
checker.verify (check);
bool all_valid = std::all_of (verifications.cbegin (), verifications.cend (), [] (auto verification) { return verification == 1; });
ASSERT_TRUE (all_valid);
last_size = size;
}
}