diff --git a/Makefile.am b/Makefile.am
index c732f81d..d6a4bd98 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,7 @@ test_libbitcoin_database_test_SOURCES = \
test/query/optional.cpp \
test/query/translate.cpp \
test/query/validate.cpp \
+ test/query/wire.cpp \
test/tables/archives/header.cpp \
test/tables/archives/input.cpp \
test/tables/archives/output.cpp \
@@ -192,7 +193,8 @@ include_bitcoin_database_impl_query_HEADERS = \
include/bitcoin/database/impl/query/optional.ipp \
include/bitcoin/database/impl/query/query.ipp \
include/bitcoin/database/impl/query/translate.ipp \
- include/bitcoin/database/impl/query/validate.ipp
+ include/bitcoin/database/impl/query/validate.ipp \
+ include/bitcoin/database/impl/query/wire.ipp
include_bitcoin_database_locksdir = ${includedir}/bitcoin/database/locks
include_bitcoin_database_locks_HEADERS = \
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index 10e05d91..f1f1c41a 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -164,6 +164,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
index b7bb295d..175e235e 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
@@ -153,6 +153,9 @@
src\query
+
+ src\query
+
src
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 89a91e7c..971bb10c 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -230,6 +230,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index afcaa58c..e40f6fad 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -379,6 +379,9 @@
include\bitcoin\database\impl\query
+
+ include\bitcoin\database\impl\query
+
include\bitcoin\database\impl
diff --git a/include/bitcoin/database/impl/query/wire.ipp b/include/bitcoin/database/impl/query/wire.ipp
new file mode 100644
index 00000000..a348e6d2
--- /dev/null
+++ b/include/bitcoin/database/impl/query/wire.ipp
@@ -0,0 +1,253 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_QUERY_WIRE_IPP
+#define LIBBITCOIN_DATABASE_QUERY_WIRE_IPP
+
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+
+// Wire serialized objects.
+// ----------------------------------------------------------------------------
+// Due to the idiotic segwit serialization of witness after output there is are
+// duplicated navigations to store_.ins and store_.input by the witness reader.
+// This normalized approach is also the most efficient.
+
+TEMPLATE
+bool CLASS::get_wire_header(byteflipper& flipper,
+ const header_link& link) const NOEXCEPT
+{
+ const auto start = flipper.get_write_position();
+ table::header::wire_header header{ {}, flipper };
+ if (!store_.header.get(link, header))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ // Genesis header parent is defaulted.
+ if (header.parent_fk == schema::header::link::terminal)
+ return true;
+
+ flipper.set_position(start);
+ table::header::wire_key key{ {}, flipper };
+ if (!store_.header.get(header.parent_fk, key))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ return true;
+}
+
+TEMPLATE
+bool CLASS::get_wire_input(byteflipper& flipper,
+ const point_link& link) const NOEXCEPT
+{
+ // [point]
+ table::point::wire_point point{ {}, flipper };
+ if (!store_.point.get(link, point))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ // [[size]script]
+ table::ins::get_input ins{};
+ table::input::wire_script script{ {}, flipper };
+ if (!store_.ins.get(link, ins) ||
+ !store_.input.get(ins.input_fk, script))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ // [sequence]
+ flipper.write_4_bytes_little_endian(ins.sequence);
+ return true;
+}
+
+TEMPLATE
+bool CLASS::get_wire_output(byteflipper& flipper,
+ const output_link& link) const NOEXCEPT
+{
+ // [value][[[size]script]]
+ table::output::wire_script out{ {}, flipper };
+ if (!store_.output.get(link, out))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ return true;
+}
+
+TEMPLATE
+bool CLASS::get_wire_witness(byteflipper& flipper,
+ const point_link& link) const NOEXCEPT
+{
+ // [count][[[size]element]]
+ table::ins::get_input ins{};
+ table::input::wire_witness wire{ {}, flipper };
+ if (!store_.ins.get(link, ins) ||
+ !store_.input.get(ins.input_fk, wire))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ return true;
+}
+
+TEMPLATE
+bool CLASS::get_wire_tx(byteflipper& flipper, const tx_link& link,
+ bool witness) const NOEXCEPT
+{
+ table::transaction::record tx{};
+ if (!store_.tx.get(link, tx))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ table::outs::record outs{};
+ outs.out_fks.resize(tx.outs_count);
+ if (!store_.outs.get(tx.outs_fk, outs))
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ // Point links are contiguous (computed).
+ const auto ins_begin = tx.point_fk;
+ const auto ins_count = tx.ins_count;
+ const auto ins_final = ins_begin + ins_count;
+ const auto witnessed = witness && (tx.heavy != tx.light);
+
+ flipper.write_4_bytes_little_endian(tx.version);
+
+ if (witnessed)
+ {
+ flipper.write_byte(system::chain::witness_marker);
+ flipper.write_byte(system::chain::witness_enabled);
+ }
+
+ flipper.write_variable(ins_count);
+ for (auto fk = ins_begin; fk < ins_final; ++fk)
+ if (!get_wire_input(flipper, fk))
+ return false;
+
+ flipper.write_variable(outs.out_fks.size());
+ for (const auto& fk: outs.out_fks)
+ if (!get_wire_output(flipper, fk))
+ return false;
+
+ if (witnessed)
+ {
+ for (auto fk = ins_begin; fk < ins_final; ++fk)
+ if (!get_wire_witness(flipper, fk))
+ return false;
+ }
+
+ flipper.write_4_bytes_little_endian(tx.locktime);
+ return true;
+}
+
+TEMPLATE
+bool CLASS::get_wire_block(byteflipper& flipper, const header_link& link,
+ bool witness) const NOEXCEPT
+{
+ if (!get_wire_header(flipper, link))
+ return false;
+
+ const auto txs = to_transactions(link);
+ if (txs.empty())
+ {
+ flipper.invalidate();
+ return false;
+ }
+
+ flipper.write_variable(txs.size());
+ for (const auto& tx_link: txs)
+ if (!get_wire_tx(flipper, tx_link, witness))
+ return false;
+
+ return true;
+}
+
+// These convenience wrappers are made practical by size caching for block and
+// tx for both nominal and witness wire encodings (and fixed size headers).
+// Intermediate objects (input, output, witness) have a size prefix
+
+TEMPLATE
+data_chunk CLASS::get_wire_header(const header_link& link) const NOEXCEPT
+{
+ using namespace system;
+ data_chunk data(chain::header::serialized_size());
+
+ stream::flip::fast ostream(data);
+ flip::bytes::fast out(ostream);
+ if (!get_wire_header(out, link) || !out)
+ return {};
+
+ return data;
+}
+
+TEMPLATE
+data_chunk CLASS::get_wire_tx(const tx_link& link, bool witness) const NOEXCEPT
+{
+ using namespace system;
+ size_t size{};
+ if (!get_tx_size(size, link, witness))
+ return {};
+
+ data_chunk data(size);
+ stream::flip::fast ostream(data);
+ flip::bytes::fast out(ostream);
+ if (!get_wire_tx(out, link, witness) || !out)
+ return {};
+
+ return data;
+}
+
+TEMPLATE
+data_chunk CLASS::get_wire_block(const header_link& link,
+ bool witness) const NOEXCEPT
+{
+ using namespace system;
+ size_t size{};
+ if (!get_block_size(size, link, witness))
+ return {};
+
+ data_chunk data(size);
+ stream::flip::fast ostream(data);
+ flip::bytes::fast out(ostream);
+ if (!get_wire_block(out, link, witness) || !out)
+ return {};
+
+ return data;
+}
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index ea8cf0da..4c9864a0 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -350,14 +350,10 @@ class query
size_t position) const NOEXCEPT;
/// Sizes.
- bool get_tx_size(size_t& out, const tx_link& link,
- bool witness) const NOEXCEPT;
- bool get_block_size(size_t& out, const header_link& link,
- bool witness) const NOEXCEPT;
- bool get_block_sizes(size_t& light, size_t& heavy,
- const header_link& link) const NOEXCEPT;
- bool get_tx_sizes(size_t& light, size_t& heavy,
- const tx_link& link) const NOEXCEPT;
+ bool get_tx_size(size_t& out, const tx_link& link, bool witness) const NOEXCEPT;
+ bool get_block_size(size_t& out, const header_link& link, bool witness) const NOEXCEPT;
+ bool get_block_sizes(size_t& light, size_t& heavy, const header_link& link) const NOEXCEPT;
+ bool get_tx_sizes(size_t& light, size_t& heavy, const tx_link& link) const NOEXCEPT;
/// Heights.
height_link get_height(const hash_digest& key) const NOEXCEPT;
@@ -374,6 +370,21 @@ class query
bool get_block_spend(uint64_t& out, const header_link& link) const NOEXCEPT;
bool get_block_fee(uint64_t& out, const header_link& link) const NOEXCEPT;
+ /// Wire.
+ /// -----------------------------------------------------------------------
+
+ bool get_wire_input(byteflipper& flipper, const point_link& link) const NOEXCEPT;
+ bool get_wire_output(byteflipper& flipper, const output_link& link) const NOEXCEPT;
+ bool get_wire_witness(byteflipper& flipper, const point_link& link) const NOEXCEPT;
+ bool get_wire_header(byteflipper& flipper, const header_link& link) const NOEXCEPT;
+ bool get_wire_tx(byteflipper& flipper, const tx_link& link, bool witness) const NOEXCEPT;
+ bool get_wire_block(byteflipper& flipper, const header_link& link,
+ bool witness) const NOEXCEPT;
+
+ data_chunk get_wire_header(const header_link& link) const NOEXCEPT;
+ data_chunk get_wire_tx(const tx_link& link, bool witness) const NOEXCEPT;
+ data_chunk get_wire_block(const header_link& link, bool witness) const NOEXCEPT;
+
/// Objects.
/// -----------------------------------------------------------------------
@@ -836,6 +847,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
#include
#include
#include
+#include
BC_POP_WARNING()
diff --git a/include/bitcoin/database/tables/archives/header.hpp b/include/bitcoin/database/tables/archives/header.hpp
index afe23fef..4b5818da 100644
--- a/include/bitcoin/database/tables/archives/header.hpp
+++ b/include/bitcoin/database/tables/archives/header.hpp
@@ -321,6 +321,45 @@ struct header
context ctx{};
uint32_t timestamp{};
};
+
+ struct wire_key
+ : public schema::header
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ const auto time_bits_nonce_size = 3u * sizeof(uint32_t);
+ const auto version_size = sizeof(uint32_t);
+ source.rewind_bytes(sk);
+ flipper.skip_bytes(version_size);
+ flipper.write_bytes(source.read_hash());
+ flipper.skip_bytes(schema::hash + time_bits_nonce_size);
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
+
+ struct wire_header
+ : public schema::header
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ const auto time_bits_nonce_size = 3u * sizeof(uint32_t);
+ const auto version_size = sizeof(uint32_t);
+ source.skip_bytes(skip_to_parent);
+ parent_fk = to_parent(source.read_little_endian());
+ flipper.write_bytes(source.read_bytes(version_size));
+ flipper.write_bytes(system::null_hash);
+ source.skip_bytes(time_bits_nonce_size);
+ flipper.write_bytes(source.read_hash());
+ source.rewind_bytes(time_bits_nonce_size + schema::hash);
+ flipper.write_bytes(source.read_bytes(time_bits_nonce_size));
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ link::integer parent_fk{};
+ };
};
} // namespace table
diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp
index d2eb144c..f9130411 100644
--- a/include/bitcoin/database/tables/archives/input.hpp
+++ b/include/bitcoin/database/tables/archives/input.hpp
@@ -175,6 +175,47 @@ struct input
const system::chain::transaction& tx_{};
};
+
+ struct wire_script
+ : public schema::input
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ // script (prefixed)
+ const auto length = source.read_size();
+ flipper.write_variable(length);
+ flipper.write_bytes(source.read_bytes(length));
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
+
+ struct wire_witness
+ : public schema::input
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ // script (skip)
+ source.skip_bytes(source.read_size());
+
+ // witness (count)
+ const auto count = source.read_size();
+ flipper.write_variable(count);
+
+ // witness (prefixed)
+ for (size_t element{}; element < count; ++element)
+ {
+ const auto length = source.read_size();
+ flipper.write_variable(length);
+ flipper.write_bytes(source.read_bytes(length));
+ }
+
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
};
BC_POP_WARNING()
diff --git a/include/bitcoin/database/tables/archives/ins.hpp b/include/bitcoin/database/tables/archives/ins.hpp
index 45620a02..2f48e527 100644
--- a/include/bitcoin/database/tables/archives/ins.hpp
+++ b/include/bitcoin/database/tables/archives/ins.hpp
@@ -132,6 +132,19 @@ struct ins
const tx::integer parent_fk{};
const system::chain::transaction& tx_{};
};
+
+ struct wire_sequence
+ : public schema::ins
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ const auto sequence_size = sizeof(uint32_t);
+ flipper.write_bytes(source.read_bytes(sequence_size));
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
};
} // namespace table
diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp
index 6b54e2f6..595fd860 100644
--- a/include/bitcoin/database/tables/archives/output.hpp
+++ b/include/bitcoin/database/tables/archives/output.hpp
@@ -46,8 +46,7 @@ struct output
inline link count() const NOEXCEPT
{
return system::possible_narrow_cast(
- tx::size +
- variable_size(value) +
+ tx::size + variable_size(value) +
script.serialized_size(true));
}
@@ -231,6 +230,27 @@ struct output
const tx::integer parent_fk{};
const system::chain::transaction& tx_{};
};
+
+ struct wire_script
+ : public schema::output
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ // skip: parent_fk
+ source.skip_bytes(tx::size);
+
+ // value (translates from variable to fixed width)
+ flipper.write_8_bytes_little_endian(source.read_variable());
+
+ // script (prefixed)
+ const auto length = source.read_size();
+ flipper.write_variable(length);
+ flipper.write_bytes(source.read_bytes(length));
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
};
BC_POP_WARNING()
diff --git a/include/bitcoin/database/tables/archives/point.hpp b/include/bitcoin/database/tables/archives/point.hpp
index 0a5517c1..aef19780 100644
--- a/include/bitcoin/database/tables/archives/point.hpp
+++ b/include/bitcoin/database/tables/archives/point.hpp
@@ -29,12 +29,19 @@ namespace libbitcoin {
namespace database {
namespace table {
+// There is no value in this table, just search keys.
struct point
: public hash_map
{
using hash_map::hashmap;
using ix = linkage;
+ static uint32_t to_index(ix::integer value) NOEXCEPT
+ {
+ using namespace system;
+ return value == ix::terminal ? chain::point::null_index : value;
+ }
+
struct record
: public schema::point
{
@@ -42,11 +49,7 @@ struct point
{
source.rewind_bytes(schema::point::sk);
hash = source.read_hash();
- index = source.read_little_endian();
-
- if (index == ix::terminal)
- index = system::chain::point::null_index;
-
+ index = to_index(source.read_little_endian());
BC_ASSERT(!source || source.get_read_position() == minrow);
return source;
}
@@ -81,7 +84,7 @@ struct point
key =
{
source.read_hash(),
- source.read_little_endian()
+ to_index(source.read_little_endian())
};
BC_ASSERT(!source || source.get_read_position() == minrow);
return source;
@@ -89,6 +92,21 @@ struct point
system::chain::point key{};
};
+
+ struct wire_point
+ : public schema::point
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ source.rewind_bytes(schema::point::sk);
+ flipper.write_bytes(source.read_hash());
+ const auto value = source.read_little_endian();
+ flipper.write_4_bytes_little_endian(to_index(value));
+ return source;
+ }
+
+ system::byteflipper& flipper;
+ };
};
} // namespace table
diff --git a/include/bitcoin/database/types.hpp b/include/bitcoin/database/types.hpp
index 00eb1383..e5fa4bae 100644
--- a/include/bitcoin/database/types.hpp
+++ b/include/bitcoin/database/types.hpp
@@ -51,6 +51,11 @@ using inpoints = std::set;
using outpoint = system::chain::outpoint;
using outpoints = std::set;
+using data_chunk = system::data_chunk;
+using bytereader = system::bytereader;
+using bytewriter = system::bytewriter;
+using byteflipper = system::byteflipper;
+
struct header_state { header_link link; code ec; };
using header_states = std::vector;
diff --git a/test/mocks/blocks.cpp b/test/mocks/blocks.cpp
index 12cfd49c..7ba6ca7a 100644
--- a/test/mocks/blocks.cpp
+++ b/test/mocks/blocks.cpp
@@ -21,117 +21,751 @@
namespace test {
+using namespace system;
+constexpr hash_digest two_hash = from_uintx(uint256_t(two));
+constexpr database::context context{ 0x01020304, 0x11121314, 0x21222324 };
+
+constexpr hash_digest block0_hash = base16_hash("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+constexpr hash_digest block1_hash = base16_hash("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
+constexpr hash_digest block2_hash = base16_hash("000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd");
+constexpr hash_digest block3_hash = base16_hash("0000000082b5015589a3fdf2d4baff403e6f0be035a5d9742c1cae6295464449");
+constexpr hash_digest block4_hash = base16_hash("000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485");
+constexpr hash_digest block5_hash = base16_hash("000000009b7262315dbf071787ad3656097b892abffd1f95a1a022f896f533fc");
+constexpr hash_digest block6_hash = base16_hash("000000003031a0e73735690c5a1ff2a4be82553b2a12b776fbd3a215dc8f778d");
+constexpr hash_digest block7_hash = base16_hash("0000000071966c2b1d065fd446b1e485b2c9d9594acd2007ccbd5441cfc89444");
+constexpr hash_digest block8_hash = base16_hash("00000000408c48f847aa786c2268fc3e6ec2af68e8468a34a28c61b7f1de0dc6");
+constexpr hash_digest block9_hash = base16_hash("000000008d9dc510f23c2657fc4f67bea30078cc05a90eb89e84cc475c080805");
+
+constexpr hash_digest root01 = sha256::double_hash(block0_hash, block1_hash);
+constexpr hash_digest root23 = sha256::double_hash(block2_hash, block3_hash);
+constexpr hash_digest root03 = sha256::double_hash(root01, root23);
+constexpr hash_digest root45 = sha256::double_hash(block4_hash, block5_hash);
+constexpr hash_digest root67 = sha256::double_hash(block6_hash, block7_hash);
+constexpr hash_digest root47 = sha256::double_hash(root45, root67);
+constexpr hash_digest root07 = sha256::double_hash(root03, root47);
+constexpr hash_digest root82 = sha256::double_hash(block8_hash, block8_hash);
+constexpr hash_digest root84 = sha256::double_hash(root82, root82);
+constexpr hash_digest root88 = sha256::double_hash(root84, root84);
+constexpr hash_digest root08 = sha256::double_hash(root07, root88);
+
+constexpr header_data header0_data = base16_array("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c");
+constexpr header_data header1_data = base16_array("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299");
+constexpr header_data header2_data = base16_array("010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd61");
+constexpr header_data header3_data = base16_array("01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d");
+constexpr header_data header4_data = base16_array("010000004944469562ae1c2c74d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a9");
+constexpr header_data header5_data = base16_array("0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477");
+constexpr header_data header6_data = base16_array("01000000fc33f596f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c97");
+constexpr header_data header7_data = base16_array("010000008d778fdc15a2d3fb76b7122a3b5582bea4f21f5a0c693537e7a03130000000003f674005103b42f984169c7d008370967e91920a6a5d64fd51282f75bc73a68af1c66649ffff001d39a59c86");
+constexpr header_data header8_data = base16_array("010000004494c8cf4154bdcc0720cd4a59d9c9b285e4b146d45f061d2b6c967100000000e3855ed886605b6d4a99d5fa2ef2e9b0b164e63df3c4136bebf2d0dac0f1f7a667c86649ffff001d1c4b5666");
+constexpr header_data header9_data = base16_array("01000000c60ddef1b7618ca2348a46e868afc26e3efc68226c78aa47f8488c4000000000c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37047fca6649ffff001d28404f53");
+
+constexpr block_data block1_data = base16_array("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");
+constexpr block_data block2_data = base16_array("010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000");
+constexpr block_data block3_data = base16_array("01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010effffffff0100f2052a0100000043410494b9d3e76c5b1629ecf97fff95d7a4bbdac87cc26099ada28066c6ff1eb9191223cd897194a08d0c2726c5747f1db49e8cf90e75dc3e3550ae9b30086f3cd5aaac00000000");
+constexpr block_data block4_data = base16_array("010000004944469562ae1c2c74d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a90101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d011affffffff0100f2052a01000000434104184f32b212815c6e522e66686324030ff7e5bf08efb21f8b00614fb7690e19131dd31304c54f37baa40db231c918106bb9fd43373e37ae31a0befc6ecaefb867ac00000000");
+constexpr block_data block5_data = base16_array("0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e4770101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0120ffffffff0100f2052a0100000043410456579536d150fbce94ee62b47db2ca43af0a730a0467ba55c79e2a7ec9ce4ad297e35cdbb8e42a4643a60eef7c9abee2f5822f86b1da242d9c2301c431facfd8ac00000000");
+constexpr block_data block6_data = base16_array("01000000fc33f596f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c970101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0123ffffffff0100f2052a0100000043410408ce279174b34c077c7b2043e3f3d45a588b85ef4ca466740f848ead7fb498f0a795c982552fdfa41616a7c0333a269d62108588e260fd5a48ac8e4dbf49e2bcac00000000");
+constexpr block_data block7_data = base16_array("010000008d778fdc15a2d3fb76b7122a3b5582bea4f21f5a0c693537e7a03130000000003f674005103b42f984169c7d008370967e91920a6a5d64fd51282f75bc73a68af1c66649ffff001d39a59c860101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d012bffffffff0100f2052a01000000434104a59e64c774923d003fae7491b2a7f75d6b7aa3f35606a8ff1cf06cd3317d16a41aa16928b1df1f631f31f28c7da35d4edad3603adb2338c4d4dd268f31530555ac00000000");
+constexpr block_data block8_data = base16_array("010000004494c8cf4154bdcc0720cd4a59d9c9b285e4b146d45f061d2b6c967100000000e3855ed886605b6d4a99d5fa2ef2e9b0b164e63df3c4136bebf2d0dac0f1f7a667c86649ffff001d1c4b56660101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d012cffffffff0100f2052a01000000434104cc8d85f5e7933cb18f13b97d165e1189c1fb3e9c98b0dd5446b2a1989883ff9e740a8a75da99cc59a21016caf7a7afd3e4e9e7952983e18d1ff70529d62e0ba1ac00000000");
+constexpr block_data block9_data = base16_array("01000000c60ddef1b7618ca2348a46e868afc26e3efc68226c78aa47f8488c4000000000c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37047fca6649ffff001d28404f530101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0134ffffffff0100f2052a0100000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000");
+
+using namespace system::chain;
+const block genesis = system::settings{ selection::mainnet }.genesis_block;
+const block block1{ block1_data, true };
+const block block2{ block2_data, true };
+const block block3{ block3_data, true };
+const block block4{ block4_data, true };
+const block block5{ block5_data, true };
+const block block6{ block6_data, true };
+const block block7{ block7_data, true };
+const block block8{ block8_data, true };
+const block block9{ block9_data, true };
+
+bool setup_three_block_store(query_t& query) NOEXCEPT
+{
+ return query.initialize(genesis) &&
+ query.set(block1, database::context{ 0, 1, 0 }, false, false) &&
+ query.set(block2, database::context{ 0, 2, 0 }, false, false) &&
+ query.push_confirmed(query.to_header(block1_hash), false) &&
+ query.push_confirmed(query.to_header(block2_hash), false);
+}
+
+bool setup_three_block_witness_store(query_t& query) NOEXCEPT
+{
+ return query.initialize(genesis) &&
+ query.set(block1a, database::context{ 0, 1, 0 }, false, false) &&
+ query.set(block2a, database::context{ 0, 2, 0 }, false, false) &&
+ query.push_confirmed(query.to_header(block1a.hash()), false) &&
+ query.push_confirmed(query.to_header(block2a.hash()), false);
+}
+
// Setting block metadata on a shared instance creates test side effects.
// Chain objects such as blocks cannot be copied for side-effect-free metadata
// tests, since block copy takes shared pointer references. So create new test
// blocks for each metadata test.
-block get_bogus_block()
+const block bogus_block
{
- return block
+ header
{
- header
+ 0x31323334,
+ null_hash,
+ one_hash,
+ 0x41424344,
+ 0x51525354,
+ 0x61626364
+ },
+ transactions
+ {
+ transaction
+ {
+ 0x01,
+ inputs
+ {
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x02
+ },
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x03
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x04,
+ script{}
+ }
+ },
+ 0x05
+ },
+ transaction
{
- 0x31323334,
- system::null_hash,
- system::one_hash,
- 0x41424344,
- 0x51525354,
- 0x61626364
+ 0x06,
+ inputs
+ {
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x07
+ },
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x08
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x09,
+ script{}
+ }
+ },
+ 0x0a
},
- transactions
- {
- transaction
- {
- 0x01,
- inputs
- {
- input
- {
- point{},
- script{},
- witness{},
- 0x02
- },
- input
- {
- point{},
- script{},
- witness{},
- 0x03
- }
+ transaction
+ {
+ 0x0b,
+ inputs
+ {
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x0c
+ },
+ input
+ {
+ point{},
+ script{},
+ witness{},
+ 0x0d
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x0e,
+ script{}
+ }
+ },
+ 0x0f
+ }
+ }
+};
+const block block1a
+{
+ header
+ {
+ 0x31323334, // version
+ block0_hash, // previous_block_hash
+ null_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ // This first transaction is *not* a coinbase.
+ transaction // tx#1
+ {
+ 0x2a, // version
+ inputs
+ {
+ input
+ {
+ point{ one_hash, 0x18 }, // missing prevout
+ script{ { { opcode::op_return }, { opcode::pick } } },
+ witness{ "[242424]" },
+ 0x2a // sequence
+ },
+ input
+ {
+ point{ one_hash, 0x2a }, // missing prevout
+ script{ { { opcode::op_return }, { opcode::roll } } },
+ witness{ "[313131]" },
+ 0x18 // sequence
+ },
+ input
+ {
+ point{ two_hash, 0x2b }, // missing prevout
+ script{ { { opcode::op_return }, { opcode::roll } } },
+ witness{ "[424242]" },
+ 0x19 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x18, // value
+ script{ { { opcode::pick } } }
},
- outputs
+ output
+ {
+ 0x2a, // value
+ script{ { { opcode::roll } } }
+ }
+ },
+ 0x18 // locktime
+ }
+ }
+};
+const block block2a
+{
+ header
+ {
+ 0x31323334, // version
+ block1a.hash(), // previous_block_hash
+ one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ // This first transaction is *not* a coinbase.
+ transaction // tx#2
+ {
+ 0xa2, // version
+ inputs
+ {
+ input
{
- output
- {
- 0x04,
- script{}
- }
+ // existing prevout
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::pick } } },
+ witness{ "[242424]" },
+ 0xa2 // sequence
},
- 0x05
- },
- transaction
- {
- 0x06,
- inputs
- {
- input
- {
- point{},
- script{},
- witness{},
- 0x07
- },
- input
- {
- point{},
- script{},
- witness{},
- 0x08
- }
+ input
+ {
+ // existing prevout
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::roll } } },
+ witness{ "[313131]" },
+ 0x81 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x81, // value
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x81 // locktime
+ },
+ transaction // tx#3
+ {
+ 0xa2, // version
+ inputs
+ {
+ input
+ {
+ point{ one_hash, 0x20 }, // missing prevout
+ script{ { { opcode::checkmultisig }, { opcode::pick } } },
+ witness{ "[242424]" },
+ 0xa2 // sequence
},
- outputs
+ input
+ {
+ point{ one_hash, 0x21 }, // missing prevout
+ script{ { { opcode::checkmultisig }, { opcode::roll } } },
+ witness{ "[313131]" },
+ 0x81 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x81, // value
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x81 // locktime
+ }
+ }
+};
+const transaction tx4
+{
+ 0xa5, // version
+ inputs
+ {
+ input
+ {
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::pick } } },
+ witness{ "[252525]" },
+ 0xa5 // sequence
+ },
+ input
+ {
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::roll } } },
+ witness{ "[353535]" },
+ 0x85 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x85, // value
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x85 // locktime
+};
+const transaction tx5
+{
+ 0xa5, // version
+ inputs
+ {
+ input
+ {
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::pick } } },
+ witness{ "[252525]" },
+ 0xa5 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x85, // value
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x85 // locktime
+};
+const block block_spend_1a
+{
+ header
+ {
+ 0x31323334, // version
+ block1a.hash(), // previous_block_hash
+ two_hash, // merkle_root (two_hash allows double spend)
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx4
+ }
+};
+const transaction tx_spend_genesis
+{
+ 0xa6,
+ inputs
+ {
+ input
+ {
+ // Spend genesis.
+ point{ genesis.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::pick } } },
+ witness{ "[262626]" },
+ 0xa6
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x86,
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x86
+};
+const block block_spend_genesis
+{
+ header
+ {
+ 0x31323334, // version
+ block0_hash, // previous_block_hash
+ null_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx_spend_genesis
+ }
+};
+const block block3a
+{
+ header
+ {
+ 0x31323334, // version
+ block2a.hash(), // previous_block_hash
+ two_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ // This first transaction is *not* a coinbase.
+ transaction
+ {
+ 0xa3, // version
+ inputs
+ {
+ input
{
- output
- {
- 0x09,
- script{}
- }
+ // existing prevout
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{ "[949494]" },
+ 0xa3 // sequence
},
- 0x0a
- },
- transaction
- {
- 0x0b,
- inputs
- {
- input
- {
- point{},
- script{},
- witness{},
- 0x0c
- },
- input
- {
- point{},
- script{},
- witness{},
- 0x0d
- }
+ input
+ {
+ // existing prevout
+ point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{ "[919191]" },
+ 0x83 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x83, // value
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0x83 // locktime
+ }
+ }
+};
+const block block1b
+{
+ header
+ {
+ 0x31323334, // version
+ block0_hash, // previous_block_hash
+ null_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ // This first transaction is a coinbase.
+ transaction // tx#1
+ {
+ 0xb1,
+ inputs
+ {
+ input
+ {
+ point{},
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb1
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb1,
+ script{ { { opcode::pick } } }
},
- outputs
+ output
+ {
+ 0xb1,
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb1
+ }
+ }
+};
+const block block2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ // This first transaction is a coinbase.
+ transaction // tx#2
+ {
+ 0xb2,
+ inputs
+ {
+ input
{
- output
- {
- 0x0e,
- script{}
- }
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
},
- 0x0f
+ input
+ {
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb2,
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
+ }
+ }
+};
+const transaction tx2b
+{
+ transaction
+ {
+ 0xb1,
+ inputs
+ {
+ input
+ {
+ // Spends block1b coinbase.
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb1
}
+ },
+ outputs
+ {
+ output
+ {
+ 0xb1,
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb1
+ }
+};
+const block block_spend_internal_2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx2b, // tx#2
+ transaction // tx#3
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ // Spends tx2b:0.
+ point{ tx2b.hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb2,
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
}
- };
-}
+ }
+};
+const block block_missing_prevout_2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx2b, // tx#2
+ transaction // tx#3
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ // missing prevout index.
+ point{ tx2b.hash(false), 0x01 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb0, // fee will be 0x01
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
+ }
+ }
+};
+const block block_valid_spend_internal_2b
+{
+ header
+ {
+ 0x31323334, // version
+ block1b.hash(), // previous_block_hash
+ one_hash, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ },
+ transactions
+ {
+ tx2b, // tx#2
+ transaction // tx#3
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ point{ tx2b.hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb0, // fee will be 0x01
+ script{ { { opcode::pick } } }
+ }
+ },
+ 0xb2
+ },
+ transaction // tx#4
+ {
+ 0xb2,
+ inputs
+ {
+ input
+ {
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
+ script{ { { opcode::checkmultisig }, { opcode::size } } },
+ witness{},
+ 0xb2
+ },
+ input
+ {
+ point{ block1b.transactions_ptr()->front()->hash(false), 0x01 },
+ script{ { { opcode::checkmultisig } } },
+ witness{},
+ 0xb2
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0xb2, // fee will be 0xb1 + 0xb1 - 0xb2 = 0xb0
+ script{ { { opcode::pick }, { opcode::roll }, { opcode::pick } } }
+ }
+ },
+ 0xb2
+ }
+ }
+};
} // namespace test
diff --git a/test/mocks/blocks.hpp b/test/mocks/blocks.hpp
index c30c483b..82bc02ce 100644
--- a/test/mocks/blocks.hpp
+++ b/test/mocks/blocks.hpp
@@ -23,608 +23,86 @@
namespace test {
-constexpr auto block0_hash = system::base16_hash("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
-constexpr auto block1_hash = system::base16_hash("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
-constexpr auto block2_hash = system::base16_hash("000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd");
-constexpr auto block3_hash = system::base16_hash("0000000082b5015589a3fdf2d4baff403e6f0be035a5d9742c1cae6295464449");
-constexpr auto block4_hash = system::base16_hash("000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485");
-constexpr auto block5_hash = system::base16_hash("000000009b7262315dbf071787ad3656097b892abffd1f95a1a022f896f533fc");
-constexpr auto block6_hash = system::base16_hash("000000003031a0e73735690c5a1ff2a4be82553b2a12b776fbd3a215dc8f778d");
-constexpr auto block7_hash = system::base16_hash("0000000071966c2b1d065fd446b1e485b2c9d9594acd2007ccbd5441cfc89444");
-constexpr auto block8_hash = system::base16_hash("00000000408c48f847aa786c2268fc3e6ec2af68e8468a34a28c61b7f1de0dc6");
+using block_data = system::data_array<215>;
+using header_data = system::data_array<80>;
+using store_t = database::store;
+using query_t = database::query>;
+const auto events_handler = [](auto, auto) {};
+extern const database::context context;
-// blockchain.info/rawblock/[block-hash]?format=hex
-constexpr auto block1_data = system::base16_array("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");
-constexpr auto block2_data = system::base16_array("010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000");
-constexpr auto block3_data = system::base16_array("01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010effffffff0100f2052a0100000043410494b9d3e76c5b1629ecf97fff95d7a4bbdac87cc26099ada28066c6ff1eb9191223cd897194a08d0c2726c5747f1db49e8cf90e75dc3e3550ae9b30086f3cd5aaac00000000");
-constexpr auto block4_data = system::base16_array("010000004944469562ae1c2c74d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a90101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d011affffffff0100f2052a01000000434104184f32b212815c6e522e66686324030ff7e5bf08efb21f8b00614fb7690e19131dd31304c54f37baa40db231c918106bb9fd43373e37ae31a0befc6ecaefb867ac00000000");
-constexpr auto block5_data = system::base16_array("0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e4770101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0120ffffffff0100f2052a0100000043410456579536d150fbce94ee62b47db2ca43af0a730a0467ba55c79e2a7ec9ce4ad297e35cdbb8e42a4643a60eef7c9abee2f5822f86b1da242d9c2301c431facfd8ac00000000");
-constexpr auto block6_data = system::base16_array("01000000fc33f596f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c970101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0123ffffffff0100f2052a0100000043410408ce279174b34c077c7b2043e3f3d45a588b85ef4ca466740f848ead7fb498f0a795c982552fdfa41616a7c0333a269d62108588e260fd5a48ac8e4dbf49e2bcac00000000");
-constexpr auto block7_data = system::base16_array("010000008d778fdc15a2d3fb76b7122a3b5582bea4f21f5a0c693537e7a03130000000003f674005103b42f984169c7d008370967e91920a6a5d64fd51282f75bc73a68af1c66649ffff001d39a59c860101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d012bffffffff0100f2052a01000000434104a59e64c774923d003fae7491b2a7f75d6b7aa3f35606a8ff1cf06cd3317d16a41aa16928b1df1f631f31f28c7da35d4edad3603adb2338c4d4dd268f31530555ac00000000");
-constexpr auto block8_data = system::base16_array("010000004494c8cf4154bdcc0720cd4a59d9c9b285e4b146d45f061d2b6c967100000000e3855ed886605b6d4a99d5fa2ef2e9b0b164e63df3c4136bebf2d0dac0f1f7a667c86649ffff001d1c4b56660101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d012cffffffff0100f2052a01000000434104cc8d85f5e7933cb18f13b97d165e1189c1fb3e9c98b0dd5446b2a1989883ff9e740a8a75da99cc59a21016caf7a7afd3e4e9e7952983e18d1ff70529d62e0ba1ac00000000");
+extern const system::hash_digest block0_hash;
+extern const system::hash_digest block1_hash;
+extern const system::hash_digest block2_hash;
+extern const system::hash_digest block3_hash;
+extern const system::hash_digest block4_hash;
+extern const system::hash_digest block5_hash;
+extern const system::hash_digest block6_hash;
+extern const system::hash_digest block7_hash;
+extern const system::hash_digest block8_hash;
+extern const system::hash_digest block9_hash;
-constexpr hash_digest two_hash = system::from_uintx(uint256_t(two));
-constexpr database::context context
-{
- 0x01020304, // flags
- 0x11121314, // height
- 0x21222324 // mtp
-};
+extern const system::hash_digest root01;
+extern const system::hash_digest root23;
+extern const system::hash_digest root03;
+extern const system::hash_digest root45;
+extern const system::hash_digest root67;
+extern const system::hash_digest root47;
+extern const system::hash_digest root07;
+extern const system::hash_digest root82;
+extern const system::hash_digest root84;
+extern const system::hash_digest root88;
+extern const system::hash_digest root08;
-using namespace system::chain;
+extern const header_data header0_data;
+extern const header_data header1_data;
+extern const header_data header2_data;
+extern const header_data header3_data;
+extern const header_data header4_data;
+extern const header_data header5_data;
+extern const header_data header6_data;
+extern const header_data header7_data;
+extern const header_data header8_data;
+extern const header_data header9_data;
-// defined in blocks.cpp.
-block get_bogus_block();
+extern const block_data block1_data;
+extern const block_data block2_data;
+extern const block_data block3_data;
+extern const block_data block4_data;
+extern const block_data block5_data;
+extern const block_data block6_data;
+extern const block_data block7_data;
+extern const block_data block8_data;
+extern const block_data block9_data;
-const auto genesis = system::settings{ selection::mainnet }.genesis_block;
-const block block1{ test::block1_data, true };
-const block block2{ test::block2_data, true };
-const block block3{ test::block3_data, true };
-const block block4{ test::block4_data, true };
-const block block5{ test::block5_data, true };
-const block block6{ test::block6_data, true };
-const block block7{ test::block7_data, true };
-const block block8{ test::block8_data, true };
+extern const system::chain::block genesis;
+extern const system::chain::block block1;
+extern const system::chain::block block2;
+extern const system::chain::block block3;
+extern const system::chain::block block4;
+extern const system::chain::block block5;
+extern const system::chain::block block6;
+extern const system::chain::block block7;
+extern const system::chain::block block8;
+extern const system::chain::block block9;
-const block block1a
-{
- header
- {
- 0x31323334, // version
- genesis.hash(), // previous_block_hash
- system::null_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- // This first transaction is *not* a coinbase.
- transaction // tx#1
- {
- 0x2a, // version
- inputs
- {
- input
- {
- point{ system::one_hash, 0x18 }, // missing prevout
- script{ { { opcode::op_return }, { opcode::pick } } },
- witness{ "[242424]" },
- 0x2a // sequence
- },
- input
- {
- point{ system::one_hash, 0x2a }, // missing prevout
- script{ { { opcode::op_return }, { opcode::roll } } },
- witness{ "[313131]" },
- 0x18 // sequence
- },
- input
- {
- point{ two_hash, 0x2b }, // missing prevout
- script{ { { opcode::op_return }, { opcode::roll } } },
- witness{ "[424242]" },
- 0x19 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x18, // value
- script{ { { opcode::pick } } }
- },
- output
- {
- 0x2a, // value
- script{ { { opcode::roll } } }
- }
- },
- 0x18 // locktime
- }
- }
-};
-const block block2a
-{
- header
- {
- 0x31323334, // version
- block1a.hash(), // previous_block_hash
- system::one_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- // This first transaction is *not* a coinbase.
- transaction // tx#2
- {
- 0xa2, // version
- inputs
- {
- input
- {
- // existing prevout
- point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::pick } } },
- witness{ "[242424]" },
- 0xa2 // sequence
- },
- input
- {
- // existing prevout
- point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
- script{ { { opcode::checkmultisig }, { opcode::roll } } },
- witness{ "[313131]" },
- 0x81 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x81, // value
- script{ { { opcode::pick } } }
- }
- },
- 0x81 // locktime
- },
- transaction // tx#3
- {
- 0xa2, // version
- inputs
- {
- input
- {
- point{ system::one_hash, 0x20 }, // missing prevout
- script{ { { opcode::checkmultisig }, { opcode::pick } } },
- witness{ "[242424]" },
- 0xa2 // sequence
- },
- input
- {
- point{ system::one_hash, 0x21 }, // missing prevout
- script{ { { opcode::checkmultisig }, { opcode::roll } } },
- witness{ "[313131]" },
- 0x81 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x81, // value
- script{ { { opcode::pick } } }
- }
- },
- 0x81 // locktime
- }
- }
-};
-const transaction tx4
-{
- 0xa5, // version
- inputs
- {
- input
- {
- point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::pick } } },
- witness{ "[252525]" },
- 0xa5 // sequence
- },
- input
- {
- point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
- script{ { { opcode::checkmultisig }, { opcode::roll } } },
- witness{ "[353535]" },
- 0x85 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x85, // value
- script{ { { opcode::pick } } }
- }
- },
- 0x85 // locktime
-};
-const transaction tx5
-{
- 0xa5, // version
- inputs
- {
- input
- {
- point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::pick } } },
- witness{ "[252525]" },
- 0xa5 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x85, // value
- script{ { { opcode::pick } } }
- }
- },
- 0x85 // locktime
-};
-const block block_spend_1a
-{
- header
- {
- 0x31323334, // version
- block1a.hash(), // previous_block_hash
- two_hash, // merkle_root (two_hash allows double spend)
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- tx4
- }
-};
-const transaction tx_spend_genesis
-{
- 0xa6,
- inputs
- {
- input
- {
- // Spend genesis.
- point{ genesis.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::pick } } },
- witness{ "[262626]" },
- 0xa6
- }
- },
- outputs
- {
- output
- {
- 0x86,
- script{ { { opcode::pick } } }
- }
- },
- 0x86
-};
-const block block_spend_genesis
-{
- header
- {
- 0x31323334, // version
- genesis.hash(), // previous_block_hash
- system::null_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- tx_spend_genesis
- }
-};
-const block block3a
-{
- header
- {
- 0x31323334, // version
- block2a.hash(), // previous_block_hash
- two_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- // This first transaction is *not* a coinbase.
- transaction
- {
- 0xa3, // version
- inputs
- {
- input
- {
- // existing prevout
- point{ block1a.transactions_ptr()->front()->hash(false), 0x01 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{ "[949494]" },
- 0xa3 // sequence
- },
- input
- {
- // existing prevout
- point{ block1a.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{ "[919191]" },
- 0x83 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x83, // value
- script{ { { opcode::pick } } }
- }
- },
- 0x83 // locktime
- }
- }
-};
-const block block1b
-{
- header
- {
- 0x31323334, // version
- genesis.hash(), // previous_block_hash
- system::null_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- // This first transaction is a coinbase.
- transaction // tx#1
- {
- 0xb1,
- inputs
- {
- input
- {
- point{},
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb1
- }
- },
- outputs
- {
- output
- {
- 0xb1,
- script{ { { opcode::pick } } }
- },
- output
- {
- 0xb1,
- script{ { { opcode::pick } } }
- }
- },
- 0xb1
- }
- }
-};
-const block block2b
-{
- header
- {
- 0x31323334, // version
- block1b.hash(), // previous_block_hash
- system::one_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- // This first transaction is a coinbase.
- transaction // tx#2
- {
- 0xb2,
- inputs
- {
- input
- {
- point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- },
- input
- {
- point{ block1b.transactions_ptr()->front()->hash(false), 0x01 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- }
- },
- outputs
- {
- output
- {
- 0xb2,
- script{ { { opcode::pick } } }
- }
- },
- 0xb2
- }
- }
-};
-const transaction tx2b
-{
- transaction
- {
- 0xb1,
- inputs
- {
- input
- {
- // Spends block1b coinbase.
- point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb1
- }
- },
- outputs
- {
- output
- {
- 0xb1,
- script{ { { opcode::pick } } }
- }
- },
- 0xb1
- }
-};
-const block block_spend_internal_2b
-{
- header
- {
- 0x31323334, // version
- block1b.hash(), // previous_block_hash
- system::one_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- tx2b, // tx#2
- transaction // tx#3
- {
- 0xb2,
- inputs
- {
- input
- {
- // Spends tx2b:0.
- point{ tx2b.hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- }
- },
- outputs
- {
- output
- {
- 0xb2,
- script{ { { opcode::pick } } }
- }
- },
- 0xb2
- }
- }
-};
-const block block_missing_prevout_2b
-{
- header
- {
- 0x31323334, // version
- block1b.hash(), // previous_block_hash
- system::one_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- tx2b, // tx#2
- transaction // tx#3
- {
- 0xb2,
- inputs
- {
- input
- {
- // missing prevout index.
- point{ tx2b.hash(false), 0x01 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- }
- },
- outputs
- {
- output
- {
- 0xb0, // fee will be 0x01
- script{ { { opcode::pick } } }
- }
- },
- 0xb2
- }
- }
-};
-const block block_valid_spend_internal_2b
-{
- header
- {
- 0x31323334, // version
- block1b.hash(), // previous_block_hash
- system::one_hash, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- },
- transactions
- {
- tx2b, // tx#2
- transaction // tx#3
- {
- 0xb2,
- inputs
- {
- input
- {
- point{ tx2b.hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- }
- },
- outputs
- {
- output
- {
- 0xb0, // fee will be 0x01
- script{ { { opcode::pick } } }
- }
- },
- 0xb2
- },
- transaction // tx#4
- {
- 0xb2,
- inputs
- {
- input
- {
- point{ block1b.transactions_ptr()->front()->hash(false), 0x00 },
- script{ { { opcode::checkmultisig }, { opcode::size } } },
- witness{},
- 0xb2
- },
- input
- {
- point{ block1b.transactions_ptr()->front()->hash(false), 0x01 },
- script{ { { opcode::checkmultisig } } },
- witness{},
- 0xb2
- }
- },
- outputs
- {
- output
- {
- 0xb2, // fee will be 0xb1 + 0xb1 - 0xb2 = 0xb0
- script{ { { opcode::pick }, { opcode::roll }, { opcode::pick } } }
- }
- },
- 0xb2
- }
- }
-};
+extern const system::chain::block bogus_block;
+extern const system::chain::block block1a;
+extern const system::chain::block block2a;
+extern const system::chain::transaction tx4;
+extern const system::chain::transaction tx5;
+extern const system::chain::block block_spend_1a;
+extern const system::chain::transaction tx_spend_genesis;
+extern const system::chain::block block_spend_genesis;
+extern const system::chain::block block3a;
+extern const system::chain::block block1b;
+extern const system::chain::block block2b;
+extern const system::chain::transaction tx2b;
+extern const system::chain::block block_spend_internal_2b;
+extern const system::chain::block block_missing_prevout_2b;
+extern const system::chain::block block_valid_spend_internal_2b;
+
+bool setup_three_block_store(query_t& query) NOEXCEPT;
+bool setup_three_block_witness_store(query_t& query) NOEXCEPT;
} // namespace test
diff --git a/test/mocks/map_store.hpp b/test/mocks/map_store.hpp
index 09517f92..7701b50d 100644
--- a/test/mocks/map_store.hpp
+++ b/test/mocks/map_store.hpp
@@ -23,9 +23,6 @@
namespace test {
-// nop event handler.
-const auto events_handler = [](auto, auto) {};
-
// store