Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix error suppression in spend key generation. #458

Merged
merged 5 commits into from
May 3, 2024
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
9 changes: 9 additions & 0 deletions include/bitcoin/database/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ enum error_t : uint8_t
unload_locked,
unload_failure,

// mmap
disk_full,
mmap_failure,
mremap_failure,
munmap_failure,
madvise_failure,
ftruncate_failure,
fsync_failure,

/// locks
transactor_lock,
process_lock,
Expand Down
15 changes: 15 additions & 0 deletions include/bitcoin/database/impl/primitives/arraymap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ bool CLASS::truncate(const Link& count) NOEXCEPT
return manager_.truncate(count);
}

// error condition
// ----------------------------------------------------------------------------

TEMPLATE
code CLASS::get_error() const NOEXCEPT
{
return manager_.get_error();
}

TEMPLATE
void CLASS::clear_error() NOEXCEPT
{
manager_.clear_error();
}

// query interface
// ----------------------------------------------------------------------------

Expand Down
15 changes: 15 additions & 0 deletions include/bitcoin/database/impl/primitives/hashmap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ Link CLASS::count() const NOEXCEPT
// query interface
// ----------------------------------------------------------------------------

TEMPLATE
code CLASS::get_error() const NOEXCEPT
{
return manager_.get_error();
}

TEMPLATE
void CLASS::clear_error() NOEXCEPT
{
manager_.clear_error();
}

// query interface
// ----------------------------------------------------------------------------

TEMPLATE
Link CLASS::top(const Link& link) const NOEXCEPT
{
Expand Down
15 changes: 15 additions & 0 deletions include/bitcoin/database/impl/primitives/manager.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ memory_ptr CLASS::get(const Link& value) const NOEXCEPT
return file_.get(link_to_position(value));
}

// Errors.
// ----------------------------------------------------------------------------

TEMPLATE
code CLASS::get_error() const NOEXCEPT
{
return file_.get_error();
}

TEMPLATE
void CLASS::clear_error() NOEXCEPT
{
file_.clear_error();
}

// private
// ----------------------------------------------------------------------------

Expand Down
87 changes: 53 additions & 34 deletions include/bitcoin/database/impl/query/archive.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -656,25 +656,6 @@ typename CLASS::inputs_ptr CLASS::get_spenders(const tx_link& link,
return get_spenders(to_output(link, output_index));
}

TEMPLATE
inline point_link CLASS::set_link_(const hash_digest& point_hash) NOEXCEPT
{
if (point_hash == system::null_hash)
return {};

// Reuse if archived (always - this is a compression, not a guard).
auto point_fk = to_point(point_hash);
if (!point_fk.is_terminal())
return point_fk;

// This write is NOT transacted as it is only called from set_link(tx)
const table::point::record empty{};
if (!store_.point.put_link(point_fk, point_hash, empty))
return {};

return point_fk;
}

// TODO: rename/change spend to archive table.
// The only multitable write, all archive except header, also address.
TEMPLATE
Expand Down Expand Up @@ -725,14 +706,38 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT
// Commit input record.
// Safe allocation failure, blob linked by unindexed spend.
input_link input_fk{};
if (!store_.input.put_link(input_fk, table::input::put_ref{ {}, *in }))
if (!store_.input.put_link(input_fk, table::input::put_ref
{
{},
*in
}))
{
return {};
}

// Create point and accumulate spend keys.
// Safe allocation failure, duplicates are guarded and expected.
// Input point aliases.
const auto& prevout = in->point();
spends.push_back(table::spend::compose(set_link_(prevout.hash()),
prevout.index()));
const auto& hash = prevout.hash();

// Get or create prevout hash in point table (reduces duplicates).
point_link hash_fk{};
if (hash != null_hash)
{
hash_fk = to_point(hash);
if (hash_fk.is_terminal())
{
// Safe allocation failure, duplicates limited but expected.
if (!store_.point.put_link(hash_fk, hash, table::point::record
{
}))
{
return {};
}
}
}

// Accumulate spend keys in order (terminal for any null point).
spends.push_back(table::spend::compose(hash_fk, prevout.index()));

// Write spend record.
// Safe allocation failure, index is deferred because invalid tx_fk.
Expand All @@ -747,7 +752,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT
return {};
}

// Acumulate input (spend) in order.
// Acumulate spends (input references) in order.
puts.spend_fks.push_back(spend_fk.value++);
}

Expand All @@ -766,7 +771,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT
return {};
}

// Acumulate output in order.
// Acumulate outputs in order.
puts.out_fks.push_back(output_fk);
}

Expand All @@ -792,10 +797,10 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT
}

// Commit spends to search.
// Safe allocation failure, unindexed txs linked by spend, others
// unlinked. A replay of committed spends without indexed tx will appear as
// double spends, but the spend cannot be confirmed without the indexed tx.
// Spends without indexed txs should be suppressed by c/s interface query.
// Safe allocation failure, unindexed txs linked by spend, others unlinked.
// A replay of committed spends without indexed tx will appear as double
// spends, but the spend cannot be confirmed without the indexed tx. Spends
// without indexed txs should be suppressed by c/s interface query.
for (const auto& spend: views_reverse(spends))
{
--spend_fk.value;
Expand Down Expand Up @@ -826,7 +831,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT
}

// Commit tx to search.
// Clean single allocation failure, see also above. (e.g. disk full).
// Clean single allocation failure (e.g. disk full).
return store_.tx.commit_link(tx_fk, key);
// ========================================================================
}
Expand Down Expand Up @@ -945,13 +950,20 @@ txs_link CLASS::set_link(const transactions& txs,
// ========================================================================
const auto scope = store_.get_transactor();

// Header link is the key for the txs table.
// Clean single allocation failure (e.g. disk full).
return store_.txs.put_link(link, table::txs::slab{ {}, malleable, wire, links });
return store_.txs.put_link(link, table::txs::slab
{
{},
malleable,
wire,
links
});
// ========================================================================
}

TEMPLATE
bool CLASS::dissasociate(const header_link& link) NOEXCEPT
bool CLASS::set_dissasociated(const header_link& link) NOEXCEPT
{
if (link.is_terminal())
return false;
Expand All @@ -961,8 +973,15 @@ bool CLASS::dissasociate(const header_link& link) NOEXCEPT
// ========================================================================
const auto scope = store_.get_transactor();

// Header link is the key for the txs table.
// Clean single allocation failure (e.g. disk full).
return store_.txs.put_link(link, table::txs::slab{ {}, malleable, {}, {} });
return store_.txs.put(link, table::txs::slab
{
{},
malleable,
{},
{}
});
// ========================================================================
}

Expand Down
6 changes: 6 additions & 0 deletions include/bitcoin/database/impl/query/query.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ CLASS::query(Store& value) NOEXCEPT
{
}

TEMPLATE
bool CLASS::is_full() const NOEXCEPT
{
return store_.get_error(error::disk_full);
}

} // namespace database
} // namespace libbitcoin

Expand Down
62 changes: 62 additions & 0 deletions include/bitcoin/database/impl/store.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
// so establish 1 as the minimum value (which also implies disabled).
constexpr auto nonzero = 1_u32;

// public
// ----------------------------------------------------------------------------

TEMPLATE
CLASS::store(const settings& config) NOEXCEPT
: configuration_(config),
Expand Down Expand Up @@ -396,6 +399,65 @@ const typename CLASS::transactor CLASS::get_transactor() NOEXCEPT
return transactor{ transactor_mutex_ };
}

TEMPLATE
bool CLASS::get_error(const code& ec) const NOEXCEPT
{
// A disk full error will not leave a flush lock, but others will.
// There may be other error codes as well so check all.

bool found{};
const auto match = [&ec, &found](const auto& storage) NOEXCEPT
{
const auto error = storage.get_error();
if (error == ec) found = true;
return !error || found;
};

return match(header_body_)
&& match(point_body_)
&& match(input_body_)
&& match(output_body_)
&& match(puts_body_)
&& match(tx_body_)
&& match(txs_body_)
&& match(address_body_)
&& match(candidate_body_)
&& match(confirmed_body_)
&& match(spend_body_)
&& match(strong_tx_body_)
&& match(validated_bk_body_)
&& match(validated_tx_body_)
&& match(neutrino_body_)
////&& match(bootstrap_body_)
////&& match(buffer_body_)
&& found;
}

TEMPLATE
void CLASS::clear_error() NOEXCEPT
{
header_body_.clear_error();
point_body_.clear_error();
input_body_.clear_error();
output_body_.clear_error();
puts_body_.clear_error();
tx_body_.clear_error();
txs_body_.clear_error();
address_body_.clear_error();
candidate_body_.clear_error();
confirmed_body_.clear_error();
spend_body_.clear_error();
strong_tx_body_.clear_error();
validated_bk_body_.clear_error();
validated_tx_body_.clear_error();
neutrino_body_.clear_error();
////bootstrap_body_.clear_error();
////buffer_body_.clear_error();
}

// protected
// ----------------------------------------------------------------------------

TEMPLATE
code CLASS::open_load(const event_handler& handler) NOEXCEPT
{
Expand Down
8 changes: 7 additions & 1 deletion include/bitcoin/database/memory/interfaces/storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class storage
virtual code load() NOEXCEPT = 0;

/// Flush memory map to disk, suspend writes for call, must be loaded.
virtual code flush() const NOEXCEPT = 0;
virtual code flush() NOEXCEPT = 0;

/// Flush, unmap and truncate to logical, restartable, idempotent.
virtual code unload() NOEXCEPT = 0;
Expand All @@ -66,6 +66,12 @@ class storage

/// Get r/w access to start/offset of memory map (or null).
virtual memory_ptr get(size_t offset=zero) const NOEXCEPT = 0;

/// Get the current error condition.
virtual code get_error() const NOEXCEPT = 0;

/// Clear the error condition.
virtual void clear_error() NOEXCEPT = 0;
};

} // namespace database
Expand Down
Loading
Loading