Skip to content
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
2 changes: 1 addition & 1 deletion include/bitcoin/database/impl/primitives/arraymap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ bool CLASS::read(const memory_ptr& ptr, const Link& link,
iostream stream{ offset, size - position };
reader source{ stream };

if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(Size);) }
if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(Size * element.count());) }
return element.from_data(source);
}

Expand Down
12 changes: 8 additions & 4 deletions include/bitcoin/database/impl/query/confirm.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ error::error_t CLASS::spent_prevout(const point_link& link, index index,
// But if we hold the spend.pk/prevout.tx we can just read the
// spend.hash/index, so we don't need to store the index, and we need to
// read the spend.hash anyway, so index is free (no paging). So that's now
// just spend[4] + tx[4], back to 8 bytes (19GB).
// just spend[4] + tx[4], back to 8 bytes (19GB). But getting spends is
// relatively cheap, just search txs[hash] and navigate puts. The downside
// is the extra search and puts is > 2x prevouts and can't be pruned.
// The upside is half the prevout size (read/write/page) and store increase.

// Iterate points by point hash (of output tx) because may be conflicts.
auto point = store_.point.it(get_point_key(link));
Expand Down Expand Up @@ -705,13 +708,14 @@ bool CLASS::set_unstrong(const header_link& link) NOEXCEPT
}

TEMPLATE
bool CLASS::set_prevouts(const header_link&, const block&) NOEXCEPT
bool CLASS::set_prevouts(size_t height, const block& block) NOEXCEPT
{
// ========================================================================
const auto scope = store_.get_transactor();

// TODO: implement.
return {};
// Clean single allocation failure (e.g. disk full).
const table::prevout::record_put_ref prevouts{ {}, block };
return store_.prevout.put(height, prevouts);
// ========================================================================
}

Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/database/query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ class query
/// Block association relies on strong (confirmed or pending).
bool set_strong(const header_link& link) NOEXCEPT;
bool set_unstrong(const header_link& link) NOEXCEPT;
bool set_prevouts(const header_link& link, const block& block) NOEXCEPT;
bool set_prevouts(size_t height, const block& block) NOEXCEPT;
code block_confirmable(const header_link& link) const NOEXCEPT;
////code tx_confirmable(const tx_link& link, const context& ctx) const NOEXCEPT;
code unspent_duplicates(const header_link& coinbase,
Expand Down
143 changes: 130 additions & 13 deletions include/bitcoin/database/tables/caches/prevout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,161 @@ namespace database {
namespace table {

/// prevout is an array map index of previous outputs by block.
/// The coinbase flag is merged into the tx field, reducing it's domain.
/// Masking is from the right in order to accomodate non-integral domain.
struct prevout
: public array_map<schema::prevout>
{
using tx = linkage<schema::tx>;
using spend = linkage<schema::spend_>;
using array_map<schema::prevout>::arraymap;
static constexpr size_t offset = sub1(to_bits(tx::size));

struct record
: public schema::prevout
{
inline bool coinbase() const NOEXCEPT
{
return system::get_right(value, offset);
}

inline tx::integer output_tx_fk() const NOEXCEPT
{
return system::set_right(value, offset, false);
}

inline void set(bool coinbase, tx::integer output_tx_fk) NOEXCEPT
{
using namespace system;
BC_ASSERT_MSG(!get_right(output_tx_fk, offset), "overflow");
value = set_right(output_tx_fk, offset, coinbase);
}

inline bool from_data(reader& source) NOEXCEPT
{
coinbase = source.read_byte();
spend_fk = source.read_little_endian<spend::integer, spend::size>();
output_tx_fk = source.read_little_endian<tx::integer, tx::size>();
value = source.read_little_endian<tx::integer, tx::size>();
BC_ASSERT(!source || source.get_read_position() == minrow);
return source;
}

inline bool to_data(finalizer& sink) const NOEXCEPT
{
sink.write_byte(coinbase);
sink.write_little_endian<spend::integer, spend::size>(spend_fk);
sink.write_little_endian<tx::integer, tx::size>(output_tx_fk);
sink.write_little_endian<tx::integer, tx::size>(value);
BC_ASSERT(!sink || sink.get_write_position() == minrow);
return sink;
}

inline bool operator==(const record& other) const NOEXCEPT
{
return coinbase == other.coinbase
&& spend_fk == other.spend_fk
&& output_tx_fk == other.output_tx_fk;
return coinbase() == other.coinbase()
&& output_tx_fk() == other.output_tx_fk();
}

tx::integer value{};
};

struct record_put_ref
: public schema::prevout
{
static constexpr tx::integer merge(bool coinbase,
tx::integer output_tx_fk) NOEXCEPT
{
using namespace system;
BC_ASSERT_MSG(!get_right(output_tx_fk, offset), "overflow");
return system::set_right(output_tx_fk, offset, coinbase);
}

// This is called once by put(), and hides base count().
inline link count() const NOEXCEPT
{
const auto spends = block.spends();
BC_ASSERT(spends < link::terminal);
return system::possible_narrow_cast<link::integer>(spends);
}

inline bool to_data(finalizer& sink) const NOEXCEPT
{
const auto txs = *block.transactions_ptr();
if (txs.size() <= one)
{
// Empty or coinbase only implies no spends.
sink.invalidate();
}
else
{
const auto write_spend = [&](const auto& in) NOEXCEPT
{
// Sets terminal sentinel for block-internal spends.
const auto value = in->metadata.inside ? tx::terminal :
merge(in->metadata.coinbase, in->metadata.parent);

sink.write_little_endian<tx::integer, tx::size>(value);
};

const auto write_tx = [&](const auto& tx) NOEXCEPT
{
const auto& ins = tx->inputs_ptr();
return std::for_each(ins->begin(), ins->end(), write_spend);
};

std::for_each(std::next(txs.begin()), txs.end(), write_tx);
}

BC_ASSERT(!sink || (sink.get_write_position() == count() * minrow));
return sink;
}

const system::chain::block& block{};
};

struct record_get
: public schema::prevout
{
// This is called once by assert, and hides base class count().
inline link count() const NOEXCEPT
{
BC_ASSERT(values.size() < link::terminal);
return system::possible_narrow_cast<link::integer>(values.size());
}

inline bool from_data(reader& source) NOEXCEPT
{
// Values must be set to read size (i.e. using knowledge of spends).
std::for_each(values.begin(), values.end(), [&](auto& value) NOEXCEPT
{
value = source.read_little_endian<tx::integer, tx::size>();
});

BC_ASSERT(!source || source.get_read_position() == count() * minrow);
return source;
}

inline bool inside(size_t index) const NOEXCEPT
{
BC_ASSERT(index < count());

// Identifies terminal sentinel as block-internal spend.
return values.at(index) == tx::terminal;
}

inline bool coinbase(size_t index) const NOEXCEPT
{
BC_ASSERT(index < count());

// Inside are always reflected as coinbase.
return system::get_right(values.at(index), offset);
}

inline tx::integer output_tx_fk(size_t index) const NOEXCEPT
{
BC_ASSERT(index < count());

// Inside are always mapped to terminal.
return inside(index) ? tx::terminal :
system::set_right(values.at(index), offset, false);
}

bool coinbase{};
spend::integer spend_fk{};
tx::integer output_tx_fk{};
// Spend count is derived in confirmation by summing block.txs.puts.
std::vector<tx::integer> values{};
};
};

Expand Down
32 changes: 16 additions & 16 deletions include/bitcoin/database/tables/schema.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,22 @@ namespace schema
/// Cache tables.
/// -----------------------------------------------------------------------

// record arraymap
struct prevout
{
static constexpr size_t pk = schema::spend_;
static constexpr size_t minsize =
////schema::bit + // merged bit into tx.
schema::tx;
static constexpr size_t minrow = minsize;
static constexpr size_t size = minsize;

// This is hidden by derivatives, to avoid virtual methods.
inline linkage<pk> count() const NOEXCEPT { return one; }
static_assert(minsize == 4u);
static_assert(minrow == 4u);
};

// slab hashmap
struct validated_bk
{
Expand Down Expand Up @@ -355,22 +371,6 @@ namespace schema
static_assert(minrow == 23u);
};

// record arraymap
struct prevout
{
static constexpr size_t pk = schema::spend_;
////static constexpr size_t sk = zero;
static constexpr size_t minsize =
schema::bit + // TODO: merge bit.
schema::spend_ +
schema::tx;
static constexpr size_t minrow = minsize;
static constexpr size_t size = minsize;
static constexpr linkage<pk> count() NOEXCEPT { return 1; }
static_assert(minsize == 9u);
static_assert(minrow == 9u);
};

// slab hashmap
struct neutrino
{
Expand Down
Loading
Loading