@@ -148,35 +148,29 @@ code CLASS::unspent_duplicates(const header_link& link,
148148 if (!ctx.is_enabled (system::chain::flags::bip30_rule))
149149 return error::success;
150150
151- // Get the block's first tx link.
151+ // Get coinbase ( block's first tx link) .
152152 const auto cb = to_coinbase (link);
153153 if (cb.is_terminal ())
154154 return error::integrity1;
155155
156- // TODO: deadlock risk.
157- // Iterate all strong records for each tx link of the same hash.
158- // The same link may be the coinbase for more than one block.
159- // Distinct links may be the coinbase for independent blocks.
160- // Duplicate instances of a tx (including cb) may exist because of a race.
161- // Strong records for a link may be reorganized and again organized.
156+ // Get all coinbases (must be at least one, because self).
162157 auto it = store_.tx .it (get_tx_key (cb));
163158 if (!it)
164159 return error::integrity2;
165160
166- // TODO: avoid nested iterators. accumulate set of tx_links and iterate set
167- // TODO: after releasing the initial iterator.
168161 tx_links coinbases{};
169- do
170- {
171- for (const auto & tx: get_strong_txs (it.self ()))
172- coinbases.push_back (tx);
173- }
174- while (it.advance ());
162+ coinbases.reserve (one);
163+ do { coinbases.push_back (it.self ()); } while (it.advance ());
175164 it.reset ();
176-
177- // Current block is set strong, so self is expected.
178- if (is_one (coinbases.size ()))
179- return error::success;
165+
166+ // Get all strong coinbases.
167+ tx_links strong_coinbases{};
168+ strong_coinbases.reserve (one);
169+ for (const auto & tx: coinbases)
170+ if (is_strong_tx (tx))
171+ strong_coinbases.push_back (tx);
172+
173+ // Strong must be other blocks, dis/re-orged blocks are not strong.
180174
181175 // Remove self (will be not found if current block is not set_strong).
182176 const auto self = std::find (coinbases.begin (), coinbases.end (), link);
@@ -193,83 +187,6 @@ code CLASS::unspent_duplicates(const header_link& link,
193187 return error::success;
194188}
195189
196- // get_double_spenders
197- // ----------------------------------------------------------------------------
198- // called from validation to set prevout state
199-
200- // protected
201- TEMPLATE
202- code CLASS::push_spenders (tx_links& out, const point& point,
203- const point_link& self) const NOEXCEPT
204- {
205- auto it = store_.point .it (table::point::compose (point));
206- if (!it)
207- {
208- // This verified that there was a race condition in the intial hashmap
209- // memory fence implementation, manifesting above as not found, where
210- // immediately after (below) the same object was found. Now fixed.
211- // //const auto key1 = it.key();
212- // //const auto link1 = it.self();
213- // //const auto get1 = it.get();
214- // //it.reset();
215- // //
216- // //if (key1 != table::point::compose(point))
217- // // return error::integrity12;
218- // //if (!link1.is_terminal())
219- // // return error::integrity13;
220- // //if (is_null(get1))
221- // // return error::integrity14;
222- // //
223- // //table::point::record get{};
224- // //if (!store_.point.get(self, get))
225- // // return error::integrity15;
226- // //if (get.hash != point.hash())
227- // // return error::integrity16;
228- // //if (get.index != point.index())
229- // // return error::integrity17;
230-
231- return error::integrity4;
232- }
233-
234- point_links points{};
235- do
236- {
237- if (it.self () != self)
238- points.push_back (it.self ());
239- }
240- while (it.advance ());
241- it.reset ();
242-
243- for (auto link: points)
244- {
245- table::ins::get_parent get{};
246- if (!store_.ins .get (link, get))
247- return error::integrity5;
248-
249- out.push_back (get.parent_fk );
250- }
251-
252- return error::success;
253- }
254-
255- TEMPLATE
256- code CLASS::get_double_spenders (tx_links& out,
257- const block& block) const NOEXCEPT
258- {
259- // Empty or coinbase only implies no spends.
260- const auto & txs = *block.transactions_ptr ();
261- if (txs.size () <= one)
262- return error::success;
263-
264- code ec{};
265- for (auto tx = std::next (txs.begin ()); tx != txs.end (); ++tx)
266- for (const auto & in: *(*tx)->inputs_ptr ())
267- if ((ec = push_spenders (out, in->point (), in->metadata .link )))
268- return ec;
269-
270- return error::success;
271- }
272-
273190// unspendable
274191// ----------------------------------------------------------------------------
275192
@@ -297,7 +214,7 @@ error::error_t CLASS::unspendable(uint32_t sequence, bool coinbase,
297214 {
298215 context out{};
299216 if (!get_context (out, strong))
300- return error::integrity7 ;
217+ return error::integrity4 ;
301218
302219 if (relative &&
303220 input::is_locked (sequence, ctx.height , ctx.mtp , out.height , out.mtp ))
@@ -325,12 +242,14 @@ code CLASS::populate_prevouts(point_sets& sets, size_t points,
325242 table::prevout::slab_get cache{};
326243 cache.spends .resize (points);
327244 if (!store_.prevout .at (link, cache))
328- return error::integrity8 ;
245+ return error::integrity5 ;
329246
247+ // The conflicts list is generally empty.
330248 for (const auto & spender: cache.conflicts )
331249 if (is_strong_tx (spender))
332250 return error::confirmed_double_spend;
333251
252+ // Augments spend.points with metadata.
334253 auto it = cache.spends .begin ();
335254 for (auto & set: sets)
336255 for (auto & point: set.points )
@@ -354,12 +273,12 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
354273
355274 context ctx{};
356275 if (!get_context (ctx, link))
357- return error::integrity9 ;
276+ return error::integrity6 ;
358277
359278 // bip30 coinbase check.
360279 code ec{};
361- // // if ((ec = unspent_duplicates(link, ctx)))
362- // // return ec;
280+ if ((ec = unspent_duplicates (link, ctx)))
281+ return ec;
363282
364283 // Empty block is success.
365284 const auto txs = to_spending_txs (link);
@@ -376,17 +295,18 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
376295 point_set set{};
377296 table::transaction::get_set_ref get{ {}, set };
378297 if (!store_.tx .get (tx, get))
379- failure.store (error::integrity10 );
298+ failure.store (error::integrity7 );
380299
381300 points.fetch_add (set.points .size (), std::memory_order_relaxed);
382301 return set;
383302 };
384303
304+ // Get points for each tx, and sum the total number.
385305 std::transform (parallel, txs.begin (), txs.end (), sets.begin (), to_set);
386306 if (failure)
387307 return { failure.load () };
388308
389- // Checks for double spends and populates prevout parent tx/cb links.
309+ // Check double spends strength, populates prevout parent tx/cb/sq links.
390310 if ((ec = populate_prevouts (sets, points, link)))
391311 return ec;
392312
@@ -401,13 +321,14 @@ code CLASS::block_confirmable(const header_link& link) const NOEXCEPT
401321 return failure != error::success;
402322 };
403323
324+ // Check all spends for spendability (strong, unlocked and mature).
404325 if (std::any_of (parallel, sets.begin (), sets.end (), is_unspendable))
405326 return { failure.load () };
406327
407328 return error::success;
408329}
409330
410- // setters
331+ // set_strong
411332// ----------------------------------------------------------------------------
412333
413334// protected
@@ -493,6 +414,59 @@ bool CLASS::set_unstrong(const header_link& link) NOEXCEPT
493414 // ========================================================================
494415}
495416
417+ // set_prevouts
418+ // ----------------------------------------------------------------------------
419+ // called from validation to set prevout state
420+
421+ // protected
422+ TEMPLATE
423+ code CLASS::push_spenders (tx_links& out, const point& point,
424+ const point_link& self) const NOEXCEPT
425+ {
426+ // This is most of the expense of confirmation.
427+ auto it = store_.point .it (table::point::compose (point));
428+ if (!it)
429+ return error::integrity8;
430+
431+ point_links points{};
432+ do
433+ {
434+ if (it.self () != self)
435+ points.push_back (it.self ());
436+ }
437+ while (it.advance ());
438+ it.reset ();
439+
440+ for (auto link: points)
441+ {
442+ table::ins::get_parent get{};
443+ if (!store_.ins .get (link, get))
444+ return error::integrity9;
445+
446+ out.push_back (get.parent_fk );
447+ }
448+
449+ return error::success;
450+ }
451+
452+ TEMPLATE
453+ code CLASS::get_double_spenders (tx_links& out,
454+ const block& block) const NOEXCEPT
455+ {
456+ // Empty or coinbase only implies no spends.
457+ const auto & txs = *block.transactions_ptr ();
458+ if (txs.size () <= one)
459+ return error::success;
460+
461+ code ec{};
462+ for (auto tx = std::next (txs.begin ()); tx != txs.end (); ++tx)
463+ for (const auto & in: *(*tx)->inputs_ptr ())
464+ if ((ec = push_spenders (out, in->point (), in->metadata .link )))
465+ return ec;
466+
467+ return error::success;
468+ }
469+
496470TEMPLATE
497471code CLASS::set_prevouts (const header_link& link, const block& block) NOEXCEPT
498472{
@@ -507,7 +481,7 @@ code CLASS::set_prevouts(const header_link& link, const block& block) NOEXCEPT
507481 // Clean single allocation failure (e.g. disk full).
508482 const table::prevout::slab_put_ref prevouts{ {}, spenders, block };
509483 return store_.prevout .put (link, prevouts) ? error::success :
510- error::integrity11 ;
484+ error::integrity10 ;
511485 // ========================================================================
512486}
513487
0 commit comments