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
1 change: 0 additions & 1 deletion include/bitcoin/database/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ enum error_t : uint8_t
integrity_get_prevouts,
integrity_block_confirmable1,
integrity_block_confirmable2,
////integrity_spent_duplicates,

/// memory map
open_open,
Expand Down
6 changes: 3 additions & 3 deletions include/bitcoin/database/impl/primitives/keys.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ INLINE uint64_t hash(const Key& key) NOEXCEPT
constexpr auto bytes = std::min(size<Key>(), sizeof(uint64_t));

uint64_t hash{};
std::copy_n(key.begin(), bytes, byte_cast(hash).begin());
std::copy_n(key.cbegin(), bytes, byte_cast(hash).begin());
return hash;
}
}
Expand Down Expand Up @@ -123,7 +123,7 @@ INLINE uint64_t thumb(const Key& key) NOEXCEPT
constexpr auto offset = std::min(size<Key>() - bytes, bytes);

uint64_t hash{};
const auto start = std::next(key.begin(), offset);
const auto start = std::next(key.cbegin(), offset);
std::copy_n(start, bytes, byte_cast(hash).begin());
return hash;
}
Expand Down Expand Up @@ -160,7 +160,7 @@ INLINE bool compare(const Array& bytes, const Key& key) NOEXCEPT
}
else if constexpr (is_std_array<Key>)
{
return std::equal(bytes.begin(), bytes.end(), key.begin());
return std::equal(bytes.cbegin(), bytes.cend(), key.begin());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace database {

// unused
TEMPLATE
code CLASS::get_unconfirmed_balance(stopper& cancel, uint64_t& out,
code CLASS::get_unconfirmed_balance(const stopper& cancel, uint64_t& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
// While duplicates are easily filtered out, conflict resolution is murky.
Expand All @@ -46,7 +46,7 @@ code CLASS::get_unconfirmed_balance(stopper& cancel, uint64_t& out,

// server/native
TEMPLATE
code CLASS::get_confirmed_balance(stopper& cancel, uint64_t& out,
code CLASS::get_confirmed_balance(const stopper& cancel, uint64_t& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
outpoints outs{};
Expand All @@ -57,7 +57,7 @@ code CLASS::get_confirmed_balance(stopper& cancel, uint64_t& out,
}

// Use of to_confirmed_unspent_outputs() provides necessary deduplication.
out = std::accumulate(outs.begin(), outs.end(), zero,
out = std::accumulate(outs.cbegin(), outs.cend(), zero,
[](size_t total, const outpoint& out) NOEXCEPT
{
return system::ceilinged_add(total, out.value());
Expand All @@ -68,7 +68,7 @@ code CLASS::get_confirmed_balance(stopper& cancel, uint64_t& out,

// server/electrum
TEMPLATE
code CLASS::get_balance(stopper& cancel, uint64_t& confirmed,
code CLASS::get_balance(const stopper& cancel, uint64_t& confirmed,
uint64_t& combined, const hash_digest& key, bool turbo) const NOEXCEPT
{
// See notes on get_unconfirmed_balance().
Expand Down
159 changes: 147 additions & 12 deletions include/bitcoin/database/impl/query/address/address_history.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,169 @@ namespace database {
// Address history
// ----------------------------------------------------------------------------
// Canonically-sorted/deduped address history.

// root txs (height:zero) sorted before transitive (height:max) txs.
// tied-height transactions sorted by base16 txid (not converted).
// All confirmed txs are root, unconfirmed may or may not be root.

// server/electrum
TEMPLATE
code CLASS::get_unconfirmed_history(stopper& , histories& ,
const hash_digest& , bool ) const NOEXCEPT
code CLASS::get_unconfirmed_history(const stopper& cancel, histories& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
return {};
output_links outs{};
if (const auto ec = to_address_outputs(cancel, outs, key))
return ec;

tx_links txs{};
if (const auto ec = to_touched_txs(cancel, txs, outs))
return ec;

out.clear();
out.resize(txs.size());
return parallel_history_transform(cancel, turbo, out, txs,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT -> history
{
if (cancel)
return {};

if (const auto block = find_strong(link); is_confirmed_block(block))
{
constexpr auto excluded = history::excluded_position;
return { system::chain::checkpoint{}, {}, excluded };
}
else
{
uint64_t fee{};
auto hash = get_tx_key(link);
if (hash == system::null_hash || !get_tx_fee(fee, link))
{
fail = true;
}

const auto height = is_confirmed_all_prevouts(link) ?
history::rooted_height : history::unrooted_height;

constexpr auto unconfirmed = history::unconfirmed_position;
return { { std::move(hash), height }, fee, unconfirmed };
}
});
}

// ununsed
TEMPLATE
code CLASS::get_confirmed_history(stopper& , histories& ,
const hash_digest& , bool ) const NOEXCEPT
code CLASS::get_confirmed_history(const stopper& cancel, histories& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
return {};
output_links outs{};
if (const auto ec = to_address_outputs(cancel, outs, key))
return ec;

tx_links txs{};
if (const auto ec = to_touched_txs(cancel, txs, outs))
return ec;

out.clear();
out.resize(txs.size());
return parallel_history_transform(cancel, turbo, out, txs,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT -> history
{
if (cancel)
return {};

if (const auto block = find_strong(link); is_confirmed_block(block))
{
uint64_t fee{};
size_t height{}, position{};
auto hash = get_tx_key(link);
if (hash == system::null_hash || !get_tx_fee(fee, link) ||
!get_height(height, block) ||
!get_tx_position(position, link, block))
{
fail = true;
}

return { { std::move(hash), height }, fee, position };
}
else
{
constexpr auto exclude = history::excluded_position;
return { system::chain::checkpoint{}, {}, exclude };
}
});
}

// server/electrum
TEMPLATE
code CLASS::get_history(stopper& , histories& ,
const hash_digest& , bool ) const NOEXCEPT
code CLASS::get_history(const stopper& cancel, histories& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
return {};
output_links outs{};
if (const auto ec = to_address_outputs(cancel, outs, key))
return ec;

tx_links links{};
if (const auto ec = to_touched_txs(cancel, links, outs))
return ec;

out.clear();
out.resize(links.size());
return parallel_history_transform(cancel, turbo, out, links,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT -> history
{
if (cancel)
return {};

uint64_t fee{};
auto hash = get_tx_key(link);
if (hash == system::null_hash || !get_tx_fee(fee, link))
fail = true;

size_t height{}, position{};
if (const auto block = find_strong(link); is_confirmed_block(block))
{
if (!get_height(height, block) ||
!get_tx_position(position, link, block))
fail = true;
}
else
{
height = is_confirmed_all_prevouts(link) ?
history::rooted_height : history::unrooted_height;
}

return { { std::move(hash), height }, fee, position };
});
}

// turbos
// utilities
// ----------------------------------------------------------------------------
// protected
// private/static

TEMPLATE
template <typename Functor>
code CLASS::parallel_history_transform(const stopper& cancel, bool turbo,
histories& out, const tx_links& txs, Functor&& functor) NOEXCEPT
{
const auto policy = poolstl::execution::par_if(turbo);
stopper fail{};

out.clear();
out.resize(txs.size());
std::transform(policy, txs.cbegin(), txs.cend(), out.begin(),
[&functor, &cancel, &fail](const auto& tx) NOEXCEPT
{
return functor(tx, cancel, fail);
});

if (fail)
return error::integrity;

if (cancel)
return error::canceled;

history::sort_and_dedup(out);
return error::success;
}

} // namespace database
} // namespace libbitcoin
Expand Down
64 changes: 35 additions & 29 deletions include/bitcoin/database/impl/query/address/address_outpoints.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,32 @@ namespace database {

// server/native
TEMPLATE
code CLASS::get_confirmed_unspent_outputs(stopper& cancel,
code CLASS::get_confirmed_unspent_outputs(const stopper& cancel,
outpoints& out, const hash_digest& key, bool turbo) const NOEXCEPT
{
out.clear();
output_links links{};
if (const code ec = to_address_outputs(cancel, links, key))
return ec;

return parallel_address_transform(cancel, turbo, out, links,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT
return parallel_outpoint_transform(cancel, turbo, out, links,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT -> outpoint
{
// !is_confirmed_unspent must be filtered out.
if (cancel || fail || !is_confirmed_unspent(link))
return outpoint{};
return {};

const auto outpoint = get_outpoint(link);
if (outpoint.point().is_null())
fail = true;

auto outpoint = get_outpoint(link);
fail = outpoint.point().is_null();
return outpoint;
});

return error::success;
}

// unused
TEMPLATE
code CLASS::get_minimum_unspent_outputs(stopper& cancel,
code CLASS::get_minimum_unspent_outputs(const stopper& cancel,
outpoints& out, const hash_digest& key, uint64_t minimum,
bool turbo) const NOEXCEPT
{
Expand All @@ -67,52 +67,55 @@ code CLASS::get_minimum_unspent_outputs(stopper& cancel,
if (const code ec = to_address_outputs(cancel, links, key))
return ec;

return parallel_address_transform(cancel, turbo, out, links,
return parallel_outpoint_transform(cancel, turbo, out, links,
[this, minimum](const auto& link, auto& cancel, auto& fail) NOEXCEPT
-> outpoint
{
// !is_confirmed_unspent must be filtered out.
if (cancel || fail || !is_confirmed_unspent(link))
return outpoint{};
return {};

uint64_t value{};
if (!get_value(value, link))
{
fail = true;
return outpoint{};
return {};
}

// Must be filtered out.
if (value < minimum)
return outpoint{};
return {};

const auto outpoint = get_outpoint(link);
if (outpoint.point().is_null())
fail = true;

auto outpoint = get_outpoint(link);
fail = outpoint.point().is_null();
return outpoint;
});

return error::success;
}

// server/native
TEMPLATE
code CLASS::get_address_outputs(stopper& cancel, outpoints& out,
code CLASS::get_address_outputs(const stopper& cancel, outpoints& out,
const hash_digest& key, bool turbo) const NOEXCEPT
{
out.clear();
output_links links{};
if (const code ec = to_address_outputs(cancel, links, key))
return ec;

return parallel_address_transform(cancel, turbo, out, links,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT
return parallel_outpoint_transform(cancel, turbo, out, links,
[this](const auto& link, auto& cancel, auto& fail) NOEXCEPT -> outpoint
{
if (cancel || fail) return outpoint{};
auto outpoint = get_outpoint(link);
fail = outpoint.point().is_null();
if (cancel || fail)
return {};

const auto outpoint = get_outpoint(link);
if (outpoint.point().is_null())
fail = true;

return outpoint;
});

return error::success;
}

// utilities
Expand All @@ -121,14 +124,16 @@ code CLASS::get_address_outputs(stopper& cancel, outpoints& out,

TEMPLATE
template <typename Functor>
inline code CLASS::parallel_address_transform(stopper& cancel, bool turbo,
code CLASS::parallel_outpoint_transform(const stopper& cancel, bool turbo,
outpoints& out, const output_links& links, Functor&& functor) NOEXCEPT
{
out.clear();
const auto policy = poolstl::execution::par_if(turbo);
stopper fail{};

out.clear();
std::vector<outpoint> outpoints(links.size());
const auto policy = poolstl::execution::par_if(turbo);
std::transform(policy, links.begin(), links.end(), outpoints.begin(),

std::transform(policy, links.cbegin(), links.cend(), outpoints.begin(),
[&functor, &cancel, &fail](const auto& link) NOEXCEPT
{
return functor(link, cancel, fail);
Expand All @@ -140,6 +145,7 @@ inline code CLASS::parallel_address_transform(stopper& cancel, bool turbo,
if (cancel)
return error::canceled;

// TODO: change outpoints to vector and avoid copy.
for (auto& outpoint: outpoints)
{
if (cancel)
Expand Down
Loading