Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-thread the signature checker #1651

Merged
merged 13 commits into from
Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,9 +634,14 @@ TEST (node_config, v15_v16_upgrade)
auto upgraded (false);
nano::node_config config;
config.logging.init (path);
ASSERT_FALSE (tree.get_optional_child ("allow_local_peers")); // allow_local_peers should not be present now
// These config options should not be present at version 15
ASSERT_FALSE (tree.get_optional_child ("allow_local_peers"));
ASSERT_FALSE (tree.get_optional_child ("signature_checker_threads"));
config.deserialize_json (upgraded, tree);
ASSERT_TRUE (!!tree.get_optional_child ("allow_local_peers")); // allow_local_peers should be added after the update
// The config options should be added after the upgrade
ASSERT_TRUE (!!tree.get_optional_child ("allow_local_peers"));
ASSERT_TRUE (!!tree.get_optional_child ("signature_checker_threads"));

ASSERT_TRUE (upgraded);
auto version (tree.get<std::string> ("version"));

Expand Down Expand Up @@ -670,18 +675,22 @@ TEST (node_config, allow_local_peers)
nano::node_config config;
config.logging.init (path);

// Check config is correct when allow_local_peers is false
// Check config is correct
tree.put ("allow_local_peers", false);
tree.put ("signature_checker_threads", 1);
config.deserialize_json (upgraded, tree);
ASSERT_FALSE (upgraded);
ASSERT_FALSE (config.allow_local_peers);
ASSERT_EQ (config.signature_checker_threads, 1);

// Check config is correct when allow_local_peers is true
// Check config is correct with other values
tree.put ("allow_local_peers", true);
tree.put ("signature_checker_threads", 4);
upgraded = false;
config.deserialize_json (upgraded, tree);
ASSERT_FALSE (upgraded);
ASSERT_TRUE (config.allow_local_peers);
ASSERT_EQ (config.signature_checker_threads, 4);
}

// Regression test to ensure that deserializing includes changes node via get_required_child
Expand Down
142 changes: 109 additions & 33 deletions nano/core_test/signing.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
#include <gtest/gtest.h>

#include <future>
#include <nano/node/node.hpp>

TEST (signature_checker, empty)
{
nano::signature_checker checker;
std::promise<void> promise;
nano::signature_check_set check = { 0, nullptr, nullptr, nullptr, nullptr, nullptr, &promise };
checker.add (check);
promise.get_future ().wait ();
nano::signature_checker checker (0);
nano::signature_check_set check = { 0, nullptr, nullptr, nullptr, nullptr, nullptr };
checker.verify (check);
}

TEST (signature_checker, many)
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;
std::promise<void> promise;
nano::signature_checker checker (0);
std::vector<nano::uint256_union> hashes;
size_t size (1000);
hashes.reserve (size);
Expand All @@ -39,34 +35,114 @@ TEST (signature_checker, many)
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 (), &promise };
checker.add (check);
promise.get_future ().wait ();
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 (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);
nano::signature_checker checker;
std::promise<void> promise;
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 (), &promise };
checker.add (check);
promise.get_future ().wait ();

// 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);
}
Loading