Skip to content

Commit 4a1a31f

Browse files
authored
Merge pull request #569 from evoskuil/master
Change prevout table to slab.
2 parents 8f49c24 + c66829c commit 4a1a31f

6 files changed

Lines changed: 247 additions & 63 deletions

File tree

include/bitcoin/database/impl/query/consensus.ipp

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ code CLASS::unspent_duplicates(const header_link& link,
199199

200200
// protected
201201
TEMPLATE
202-
bool CLASS::get_conflicts(point_links& points, const point& point,
202+
code CLASS::get_conflicts(point_links& points, const point& point,
203203
const point_link& self) const NOEXCEPT
204204
{
205205
// Iterate to all matching spend table keys. Since these are stubs of the
@@ -213,28 +213,28 @@ bool CLASS::get_conflicts(point_links& points, const point& point,
213213
// the existence of multiple spends of outputs in the same transaction.
214214
auto it = store_.spend.it(table::spend::compose(point));
215215
if (!it)
216-
return false;
216+
return error::integrity4;
217217

218218
do
219219
{
220220
table::spend::record get{};
221221
if (!store_.spend.get(it, get))
222-
return false;
222+
return error::integrity5;
223223

224224
if (get.point_fk != self)
225225
points.push_back(get.point_fk);
226226
}
227227
while (it.advance());
228-
return true;
228+
return error::success;
229229
}
230230

231231
// protected
232232
TEMPLATE
233-
bool CLASS::push_doubles(tx_links& out, const point& point,
233+
code CLASS::push_doubles(tx_links& out, const point& point,
234234
const point_links& points) const NOEXCEPT
235235
{
236236
if (points.empty())
237-
return true;
237+
return error::success;
238238

239239
// The expected self spend and primary conflicts are removed. This serves
240240
// to remove secondary conflicts, leaving only actual additional spends.
@@ -243,39 +243,44 @@ bool CLASS::push_doubles(tx_links& out, const point& point,
243243
{
244244
table::point::get_parent_key parent_tx{};
245245
if (!store_.point.get(ptr, link, parent_tx))
246-
return false;
246+
return error::integrity6;
247247

248248
if (parent_tx.hash == point.hash())
249249
out.push_back(parent_tx.fk);
250250
}
251251

252-
return true;
252+
return error::success;
253253
}
254254

255255
// protected
256256
TEMPLATE
257-
bool CLASS::push_spenders(tx_links& out, const point& point,
257+
code CLASS::push_spenders(tx_links& out, const point& point,
258258
const point_link& self) const NOEXCEPT
259259
{
260+
code ec{};
260261
point_links points{};
261-
return get_conflicts(points, point, self) &&
262-
push_doubles(out, point, points);
262+
if ((ec = get_conflicts(points, point, self)))
263+
return ec;
264+
265+
return push_doubles(out, point, points);
263266
}
264267

265268
TEMPLATE
266-
bool CLASS::get_double_spenders(tx_links& out, const block& block) const NOEXCEPT
269+
code CLASS::get_double_spenders(tx_links& out,
270+
const block& block) const NOEXCEPT
267271
{
268272
// Empty or coinbase only implies no spends.
269273
const auto& txs = *block.transactions_ptr();
270274
if (txs.size() <= one)
271-
return true;
275+
return error::success;
272276

277+
code ec{};
273278
for (auto tx = std::next(txs.begin()); tx != txs.end(); ++tx)
274279
for (const auto& in: *(*tx)->inputs_ptr())
275-
if (!push_spenders(out, in->point(), in->metadata.link))
276-
return false;
280+
if ((ec = push_spenders(out, in->point(), in->metadata.link)))
281+
return ec;
277282

278-
return true;
283+
return error::success;
279284
}
280285

281286
// unspendable
@@ -296,16 +301,16 @@ error::error_t CLASS::unspendable(uint32_t sequence, bool coinbase,
296301
return error::unconfirmed_spend;
297302
}
298303

299-
const auto bip68 = ctx.is_enabled(system::chain::flags::bip68_rule) &&
300-
(version >= system::chain::relative_locktime_min_version);
304+
const auto relative = ctx.is_enabled(system::chain::flags::bip68_rule) &&
305+
transaction::is_relative_locktime_applied(coinbase, version, sequence);
301306

302-
if (bip68 || coinbase)
307+
if (relative || coinbase)
303308
{
304309
context out{};
305310
if (!get_context(out, strong))
306-
return error::integrity4;
311+
return error::integrity7;
307312

308-
if (bip68 &&
313+
if (relative &&
309314
input::is_locked(sequence, ctx.height, ctx.mtp, out.height, out.mtp))
310315
return error::relative_time_locked;
311316

@@ -328,10 +333,10 @@ code CLASS::populate_prevouts(point_sets& sets, size_t points,
328333
if (sets.empty())
329334
return error::success;
330335

331-
table::prevout::record_get cache{};
336+
table::prevout::slab_get cache{};
332337
cache.spends.resize(points);
333338
if (!store_.prevout.at(link, cache))
334-
return error::integrity5;
339+
return error::integrity8;
335340

336341
for (const auto& spender: cache.conflicts)
337342
if (is_strong_tx(spender))
@@ -342,8 +347,8 @@ code CLASS::populate_prevouts(point_sets& sets, size_t points,
342347
for (auto& point: set.points)
343348
{
344349
const auto& pair = *it++;
345-
point.tx = table::prevout::record_get::output_tx_fk(pair.first);
346-
point.coinbase = table::prevout::record_get::coinbase(pair.first);
350+
point.tx = table::prevout::slab_get::output_tx_fk(pair.first);
351+
point.coinbase = table::prevout::slab_get::coinbase(pair.first);
347352
point.sequence = pair.second;
348353
}
349354

@@ -360,7 +365,7 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
360365

361366
context ctx{};
362367
if (!get_context(ctx, link))
363-
return error::integrity6;
368+
return error::integrity9;
364369

365370
// bip30 coinbase check.
366371
code ec{};
@@ -382,7 +387,7 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
382387
point_set set{};
383388
table::transaction::get_set_ref get{ {}, set };
384389
if (!store_.tx.get(tx, get))
385-
failure.store(error::integrity7);
390+
failure.store(error::integrity10);
386391

387392
points.fetch_add(set.points.size(), std::memory_order_relaxed);
388393
return set;
@@ -473,18 +478,20 @@ bool CLASS::set_unstrong(const header_link& link) NOEXCEPT
473478
}
474479

475480
TEMPLATE
476-
bool CLASS::set_prevouts(const header_link& link, const block& block) NOEXCEPT
481+
code CLASS::set_prevouts(const header_link& link, const block& block) NOEXCEPT
477482
{
483+
code ec{};
478484
tx_links spenders{};
479-
if (!get_double_spenders(spenders, block))
480-
return false;
485+
if ((ec = get_double_spenders(spenders, block)))
486+
return ec;
481487

482488
// ========================================================================
483489
const auto scope = store_.get_transactor();
484490

485491
// Clean single allocation failure (e.g. disk full).
486-
const table::prevout::record_put_ref prevouts{ {}, spenders, block };
487-
return store_.prevout.put(link, prevouts);
492+
const table::prevout::slab_put_ref prevouts{ {}, spenders, block };
493+
return store_.prevout.put(link, prevouts) ? error::success :
494+
error::integrity11;
488495
// ========================================================================
489496
}
490497

include/bitcoin/database/impl/query/validate.ipp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ inline code CLASS::to_block_code(
4646
case schema::block_state::unconfirmable:
4747
return error::block_unconfirmable;
4848
// Fault: Has no state, should not happen when read from store.
49+
// block_unknown also used to reset a state (debugging).
50+
case schema::block_state::block_unknown:
4951
default:
5052
return error::unknown_state;
5153
}
@@ -68,6 +70,8 @@ inline code CLASS::to_tx_code(
6870
case schema::tx_state::disconnected:
6971
return error::tx_disconnected;
7072
// Fault: Has no state, should not happen when read from store.
73+
// tx_unknown also used to reset a state (debugging).
74+
case schema::tx_state::tx_unknown:
7175
default:
7276
return error::unknown_state;
7377
}
@@ -296,6 +300,40 @@ bool CLASS::set_block_unconfirmable(const header_link& link) NOEXCEPT
296300
// ========================================================================
297301
}
298302

303+
TEMPLATE
304+
bool CLASS::set_block_unknown(const header_link& link) NOEXCEPT
305+
{
306+
// ========================================================================
307+
const auto scope = store_.get_transactor();
308+
309+
// Clean single allocation failure (e.g. disk full).
310+
return store_.validated_bk.put(link, table::validated_bk::slab
311+
{
312+
{},
313+
schema::block_state::block_unknown,
314+
0 // fees
315+
});
316+
// ========================================================================
317+
}
318+
319+
TEMPLATE
320+
bool CLASS::set_tx_unknown(const tx_link& link) NOEXCEPT
321+
{
322+
// ========================================================================
323+
const auto scope = store_.get_transactor();
324+
325+
// Clean single allocation failure (e.g. disk full).
326+
return store_.validated_tx.put(link, table::validated_tx::slab
327+
{
328+
{},
329+
{},
330+
schema::tx_state::tx_unknown,
331+
0, // fee
332+
0 // sigops
333+
});
334+
// ========================================================================
335+
}
336+
299337
TEMPLATE
300338
bool CLASS::set_tx_disconnected(const tx_link& link,
301339
const context& ctx) NOEXCEPT

include/bitcoin/database/query.hpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ class query
450450
bool set_block_valid(const header_link& link, uint64_t fees) NOEXCEPT;
451451
bool set_block_unconfirmable(const header_link& link) NOEXCEPT;
452452
bool set_block_confirmable(const header_link& link) NOEXCEPT;
453+
bool set_block_unknown(const header_link& link) NOEXCEPT;
454+
bool set_tx_unknown(const tx_link& link) NOEXCEPT;
453455
bool set_tx_disconnected(const tx_link& link, const context& ctx) NOEXCEPT;
454456
bool set_tx_connected(const tx_link& link, const context& ctx,
455457
uint64_t fee, size_t sigops) NOEXCEPT;
@@ -486,7 +488,7 @@ class query
486488

487489
bool set_strong(const header_link& link) NOEXCEPT;
488490
bool set_unstrong(const header_link& link) NOEXCEPT;
489-
bool set_prevouts(const header_link& link, const block& block) NOEXCEPT;
491+
code set_prevouts(const header_link& link, const block& block) NOEXCEPT;
490492

491493
/// Height indexation.
492494
/// -----------------------------------------------------------------------
@@ -560,13 +562,13 @@ class query
560562
code populate_prevouts(point_sets& sets, size_t points,
561563
const header_link& link) const NOEXCEPT;
562564

563-
bool get_conflicts(point_links& points, const point& point,
565+
code get_conflicts(point_links& points, const point& point,
564566
const point_link& self) const NOEXCEPT;
565-
bool push_doubles(tx_links& out, const point& point,
567+
code push_doubles(tx_links& out, const point& point,
566568
const point_links& points) const NOEXCEPT;
567-
bool push_spenders(tx_links& out, const point& point,
569+
code push_spenders(tx_links& out, const point& point,
568570
const point_link& self) const NOEXCEPT;
569-
bool get_double_spenders(tx_links& out, const block& block) const NOEXCEPT;
571+
code get_double_spenders(tx_links& out, const block& block) const NOEXCEPT;
570572

571573
error::error_t unspendable(uint32_t sequence, bool coinbase,
572574
const tx_link& prevout_tx, uint32_t version,

include/bitcoin/database/tables/caches/prevout.hpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace table {
3131

3232
/// prevout is an array map index of previous outputs by block link.
3333
/// The coinbase flag is merged into the tx field, reducing it's domain.
34-
/// Masking is from the right in order to accomodate non-integral domain.
34+
/// Masking is from the right in order to accomodate integral tx domain.
3535
struct prevout
3636
: public array_map<schema::prevout>
3737
{
@@ -44,12 +44,12 @@ struct prevout
4444
static_assert(tx::size == sizeof(uint32_t), "sequence-tx overload error");
4545

4646
// This supports only a single record (not too useful).
47-
struct record
47+
struct slab
4848
: public schema::prevout
4949
{
5050
inline link count() const NOEXCEPT
5151
{
52-
return one;
52+
return tx::size;
5353
}
5454

5555
inline bool coinbase() const NOEXCEPT
@@ -83,7 +83,7 @@ struct prevout
8383
return sink;
8484
}
8585

86-
inline bool operator==(const record& other) const NOEXCEPT
86+
inline bool operator==(const slab& other) const NOEXCEPT
8787
{
8888
return coinbase() == other.coinbase()
8989
&& output_tx_fk() == other.output_tx_fk();
@@ -92,15 +92,16 @@ struct prevout
9292
tx::integer prevout_tx{};
9393
};
9494

95-
struct record_put_ref
95+
struct slab_put_ref
9696
: public schema::prevout
9797
{
9898
inline link count() const NOEXCEPT
9999
{
100100
// TODO: assert overflow.
101101
using namespace system;
102-
return add1(possible_narrow_cast<tx::integer>(conflicts.size())) +
103-
two * possible_narrow_cast<tx::integer>(block.spends());
102+
const auto conflicts_ = conflicts.size();
103+
return variable_size(conflicts_) + (conflicts_ * tx::size) +
104+
(block.spends() * (tx::size + sizeof(uint32_t)));
104105
}
105106

106107
static constexpr tx::integer merge(bool coinbase,
@@ -140,7 +141,7 @@ struct prevout
140141
BC_ASSERT_MSG(txs.size() > one, "empty block");
141142

142143
// Count is written as a tx link so the table can remain an array.
143-
sink.write_little_endian<tx::integer, tx::size>(number);
144+
sink.write_variable(number);
144145
std::for_each(cons.begin(), cons.end(), write_con);
145146
std::for_each(std::next(txs.begin()), txs.end(), write_tx);
146147

@@ -152,21 +153,22 @@ struct prevout
152153
const system::chain::block& block{};
153154
};
154155

155-
struct record_get
156+
struct slab_get
156157
: public schema::prevout
157158
{
158159
inline link count() const NOEXCEPT
159160
{
160161
// TODO: assert overflow.
161162
using namespace system;
162-
return possible_narrow_cast<tx::integer>(add1(conflicts.size())) +
163-
two * possible_narrow_cast<tx::integer>(spends.size());
163+
const auto conflicts_ = conflicts.size();
164+
return variable_size(conflicts_) + (conflicts_ * tx::size) +
165+
(spends.size() * (tx::size + sizeof(uint32_t)));
164166
}
165167

166168
inline bool from_data(reader& source) NOEXCEPT
167169
{
168170
auto& cons = conflicts;
169-
cons.resize(source.read_little_endian<tx::integer, tx::size>());
171+
cons.resize(source.read_variable());
170172
std::for_each(cons.begin(), cons.end(), [&](auto& value) NOEXCEPT
171173
{
172174
value = source.read_little_endian<tx::integer, tx::size>();

0 commit comments

Comments
 (0)