diff --git a/include/bitcoin/database/impl/query/signatures.ipp b/include/bitcoin/database/impl/query/signatures.ipp index a766332f0..ac58decfd 100644 --- a/include/bitcoin/database/impl/query/signatures.ipp +++ b/include/bitcoin/database/impl/query/signatures.ipp @@ -26,46 +26,87 @@ namespace libbitcoin { namespace database { TEMPLATE -bool CLASS::set_signature(const hash_digest& , const ec_compressed& , - const ec_signature& , const header_link& ) NOEXCEPT +bool CLASS::verify_ecdsa_signatures(header_links& links) NOEXCEPT +{ + using batch = system::ecdsa::batch; + const auto count = store_.ecdsa.count().value; + const auto ptr = store_.ecdsa.get_memory(); + const auto rows = system::pointer_cast(ptr->data()); + links = batch::verify({ rows, count }, store_.turbo()); + return true; +} + +TEMPLATE +bool CLASS::verify_schnorr_signatures(header_links& links) NOEXCEPT +{ + using batch = system::schnorr::batch; + const auto count = store_.schnorr.count().value; + const auto ptr = store_.schnorr.get_memory(); + const auto rows = system::pointer_cast(ptr->data()); + links = batch::verify({ rows, count }, store_.turbo()); + return true; +} + +// setters +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::purge_ecdsa_signatures() NOEXCEPT { // ======================================================================== const auto scope = store_.get_transactor(); + return store_.ecdsa.truncate(0); + // ======================================================================== +} - ////// Clean single allocation failure (e.g. disk full). - ////return store_.ecdsa.put(table::ecdsa::record - ////{ - //// {}, - //// digest, - //// point, - //// signature, - //// link - ////}); +TEMPLATE +bool CLASS::purge_schnorr_signatures() NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + return store_.schnorr.truncate(0); + // ======================================================================== +} + +TEMPLATE +bool CLASS::set_signature(const hash_digest& digest, const ec_compressed& point, + const ec_signature& signature, const header_link& link) NOEXCEPT +{ + // ======================================================================== + const auto scope = store_.get_transactor(); + + // Clean single allocation failure (e.g. disk full). + return store_.ecdsa.put(table::ecdsa::record + { + {}, + digest, + point, + signature, + link + }); // ======================================================================== - // false will result in local signature validation (performance). return true; } TEMPLATE -bool CLASS::set_signature(const hash_digest& , const ec_xonly& , - const ec_signature& , const header_link& ) NOEXCEPT +bool CLASS::set_signature(const hash_digest& digest, const ec_xonly& point, + const ec_signature& signature, const header_link& link) NOEXCEPT { // ======================================================================== const auto scope = store_.get_transactor(); - ////// Clean single allocation failure (e.g. disk full). - ////return store_.schnorr.put(table::schnorr::record - ////{ - //// {}, - //// digest, - //// point, - //// signature, - //// link - ////}); + // Clean single allocation failure (e.g. disk full). + return store_.schnorr.put(table::schnorr::record + { + {}, + digest, + point, + signature, + link + }); // ======================================================================== - // false will result in local signature validation (performance). return true; } @@ -76,6 +117,8 @@ bool CLASS::set_signatures(const hash_digest&, const ec_compresseds&, // ======================================================================== const auto scope = store_.get_transactor(); + // TODO: flatten via store_.ecdsa.put(); + ////// Clean single allocation failure (e.g. disk full). ////return store_.multisig.put(table::multisig::put_ref ////{ @@ -88,7 +131,6 @@ bool CLASS::set_signatures(const hash_digest&, const ec_compresseds&, ////}); // ======================================================================== - // false will result in local signature validation (performance). return true; } @@ -99,6 +141,8 @@ bool CLASS::set_signatures(const threshold& , size_t , // ======================================================================== const auto scope = store_.get_transactor(); + // TODO: flatten via store_.schnorr.put(); + ////// Clean single allocation failure (e.g. disk full). ////return store_.multisig.put(table::multisig::put_ref ////{ @@ -111,7 +155,6 @@ bool CLASS::set_signatures(const threshold& , size_t , ////}); // ======================================================================== - // false will result in script validation failure (consensus). return true; } diff --git a/include/bitcoin/database/impl/store/store_prune.ipp b/include/bitcoin/database/impl/store/store_prune.ipp index aba425b09..82ed47117 100644 --- a/include/bitcoin/database/impl/store/store_prune.ipp +++ b/include/bitcoin/database/impl/store/store_prune.ipp @@ -62,7 +62,7 @@ code CLASS::prune(const event_handler& handler) NOEXCEPT { // zeroize table body, set logical body count to zero. handler(event_t::prune_table, table_t::prevout_body); - if (!prevout_body_.truncate(zero)) + if (!prevout_body_.truncate(0)) { ec = error::prune_table; } diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index fa91e44af..3a6ddd1c4 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -582,13 +582,11 @@ class query bool set_signatures(const threshold& group, size_t set, const header_link& link) NOEXCEPT; - // TODO: - bool purge_signatures() NOEXCEPT { return {}; }; - bool purge_ecdsa_signatures() NOEXCEPT { return {}; }; - bool purge_schnorr_signatures() NOEXCEPT { return {}; }; - ////bool verify_signatures(header_links& ) NOEXCEPT { return {}; }; - bool verify_ecdsa_signatures(header_links&) NOEXCEPT { return {}; }; - bool verify_schnorr_signatures(header_links&) NOEXCEPT { return {}; }; + /// Signature verification. + bool purge_ecdsa_signatures() NOEXCEPT; + bool purge_schnorr_signatures() NOEXCEPT; + bool verify_ecdsa_signatures(header_links&) NOEXCEPT; + bool verify_schnorr_signatures(header_links&) NOEXCEPT; /// Confirmation. /// ----------------------------------------------------------------------- diff --git a/test/query/signatures.cpp b/test/query/signatures.cpp index d4097cb66..22641390b 100644 --- a/test/query/signatures.cpp +++ b/test/query/signatures.cpp @@ -22,4 +22,158 @@ BOOST_FIXTURE_TEST_SUITE(query_signatures_tests, test::directory_setup_fixture) +using namespace system; + +const hash_digest sighash_bad = base16_array( + "4242424242424242424242424242424242424242424242424242424242424242"); + +// ecdsa + +const hash_digest ecdsa_sighash = base16_array( + "504d68beac187dd0b259ddd6ed6d5d6348150b9b23ee6dfdb43e87f74dd3c547"); +const ec_compressed ecdsa_compressed = base16_array( + "039cfcfe4a5d0efad27382e5d2b478eb398a8b691a66e01c878b600b5042b33166"); +const ec_signature ecdsa_signature = base16_array( + "b434c7c720d63d71e1136d740df7ff636770ce59cb0389ae8cd24c0d4441f143" + "01f4e1dbc36f32b4683faeecc8e4b2c6810da69e98fd783f1aad105636c3da08"); + +BOOST_AUTO_TEST_CASE(query__verify_ecdsa_signatures__empty__empty) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.ecdsa_records(), 0u); + BOOST_REQUIRE(query.verify_ecdsa_signatures(links)); + BOOST_REQUIRE(links.empty()); +} + +BOOST_AUTO_TEST_CASE(query__verify_ecdsa_signatures__one_valid__empty) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.ecdsa_records(), 1u); + BOOST_REQUIRE(query.verify_ecdsa_signatures(links)); + BOOST_REQUIRE(links.empty()); +} + +BOOST_AUTO_TEST_CASE(query__verify_ecdsa_signatures__one_invalid__expected_link) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + constexpr auto expected = 42u; + BOOST_REQUIRE(query.set_signature(sighash_bad, ecdsa_compressed, ecdsa_signature, expected)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.ecdsa_records(), 1u); + BOOST_REQUIRE(query.verify_ecdsa_signatures(links)); + BOOST_REQUIRE_EQUAL(links.size(), 1u); + BOOST_REQUIRE_EQUAL(links.front(), expected); +} + +BOOST_AUTO_TEST_CASE(query__verify_ecdsa_signatures__various__expected_links) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + constexpr auto expected1 = 42u; + constexpr auto expected2 = 24u; + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + BOOST_REQUIRE(query.set_signature(sighash_bad, ecdsa_compressed, ecdsa_signature, expected1)); + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + BOOST_REQUIRE(query.set_signature(sighash_bad, ecdsa_compressed, ecdsa_signature, expected2)); + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + BOOST_REQUIRE(query.set_signature(ecdsa_sighash, ecdsa_compressed, ecdsa_signature, 42)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.ecdsa_records(), 8u); + BOOST_REQUIRE(query.verify_ecdsa_signatures(links)); + BOOST_REQUIRE_EQUAL(links.size(), 2u); + BOOST_REQUIRE_EQUAL(links.front(), expected1); + BOOST_REQUIRE_EQUAL(links.back(), expected2); +} + +// schnorr + +// schnorr (valid BIP-340 test vector #0) +const hash_digest schnorr_sighash = base16_array( + "0000000000000000000000000000000000000000000000000000000000000000"); +const ec_xonly schnorr_xonly = base16_array( + "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9"); +const ec_signature schnorr_signature = base16_array( + "e907831f80848d1069a5371b402410364bdf1c5f8307b0084c55f1ce2dca8215" + "25f66a4a85ea8b71e482a74f382d2ce5ebeee8fdb2172f477df4900d310536c0"); + +BOOST_AUTO_TEST_CASE(query__verify_schnorr_signatures__empty__empty) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.schnorr_records(), 0u); + BOOST_REQUIRE(query.verify_schnorr_signatures(links)); + BOOST_REQUIRE(links.empty()); +} + +BOOST_AUTO_TEST_CASE(query__verify_schnorr_signatures__one_valid__empty) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.schnorr_records(), 1u); + BOOST_REQUIRE(query.verify_schnorr_signatures(links)); + BOOST_REQUIRE(links.empty()); +} + +BOOST_AUTO_TEST_CASE(query__verify_schnorr_signatures__one_invalid__expected_link) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + constexpr auto expected = 42u; + BOOST_REQUIRE(query.set_signature(sighash_bad, schnorr_xonly, schnorr_signature, expected)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.schnorr_records(), 1u); + BOOST_REQUIRE(query.verify_schnorr_signatures(links)); + BOOST_REQUIRE_EQUAL(links.size(), 1u); + BOOST_REQUIRE_EQUAL(links.front(), expected); +} + +BOOST_AUTO_TEST_CASE(query__verify_schnorr_signatures__various__expected_links) +{ + const database::settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + constexpr auto expected1 = 42u; + constexpr auto expected2 = 24u; + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + BOOST_REQUIRE(query.set_signature(sighash_bad, schnorr_xonly, schnorr_signature, expected1)); + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + BOOST_REQUIRE(query.set_signature(sighash_bad, schnorr_xonly, schnorr_signature, expected2)); + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + BOOST_REQUIRE(query.set_signature(schnorr_sighash, schnorr_xonly, schnorr_signature, 42)); + + header_links links{}; + BOOST_REQUIRE_EQUAL(query.schnorr_records(), 8u); + BOOST_REQUIRE(query.verify_schnorr_signatures(links)); + BOOST_REQUIRE_EQUAL(links.size(), 2u); + BOOST_REQUIRE_EQUAL(links.front(), expected1); + BOOST_REQUIRE_EQUAL(links.back(), expected2); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/tables/caches/ecdsa.cpp b/test/tables/caches/ecdsa.cpp index 906ee3d86..433af3bce 100644 --- a/test/tables/caches/ecdsa.cpp +++ b/test/tables/caches/ecdsa.cpp @@ -96,4 +96,28 @@ BOOST_AUTO_TEST_CASE(ecdsa__get__two__expected) BOOST_REQUIRE(out == record2); } +BOOST_AUTO_TEST_CASE(ecdsa__truncate__from_two__expected) +{ + auto head = expected_head; + auto body = expected_body; + test::chunk_storage head_store{ head }; + test::chunk_storage body_store{ body }; + table::ecdsa instance{ head_store, body_store }; + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body); + + BOOST_REQUIRE_EQUAL(instance.count(), 2u); + BOOST_REQUIRE(instance.truncate(1)); + BOOST_REQUIRE_EQUAL(instance.count(), 1u); + + table::ecdsa::record out{}; + BOOST_REQUIRE(!instance.get(1u, out)); + BOOST_REQUIRE(instance.get(0u, out)); + BOOST_REQUIRE(out == record1); + + BOOST_REQUIRE(instance.truncate(0)); + BOOST_REQUIRE_EQUAL(instance.count(), 0u); + BOOST_REQUIRE(!instance.get(0u, out)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/tables/caches/schnorr.cpp b/test/tables/caches/schnorr.cpp index 9b412ce4d..f9fee9aa1 100644 --- a/test/tables/caches/schnorr.cpp +++ b/test/tables/caches/schnorr.cpp @@ -96,4 +96,28 @@ BOOST_AUTO_TEST_CASE(schnorr__get__two__expected) BOOST_REQUIRE(out == record2); } +BOOST_AUTO_TEST_CASE(schnorr__truncate__from_two__expected) +{ + auto head = expected_head; + auto body = expected_body; + test::chunk_storage head_store{ head }; + test::chunk_storage body_store{ body }; + table::schnorr instance{ head_store, body_store }; + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body); + + BOOST_REQUIRE_EQUAL(instance.count(), 2u); + BOOST_REQUIRE(instance.truncate(1)); + BOOST_REQUIRE_EQUAL(instance.count(), 1u); + + table::schnorr::record out{}; + BOOST_REQUIRE(!instance.get(1u, out)); + BOOST_REQUIRE(instance.get(0u, out)); + BOOST_REQUIRE(out == record1); + + BOOST_REQUIRE(instance.truncate(0)); + BOOST_REQUIRE_EQUAL(instance.count(), 0u); + BOOST_REQUIRE(!instance.get(0u, out)); +} + BOOST_AUTO_TEST_SUITE_END()