Skip to content

Commit 5de742a

Browse files
authored
Merge pull request #1622 from evoskuil/master
Updates to block::populate and locktime fn names.
2 parents 4074620 + 3c8ae32 commit 5de742a

12 files changed

Lines changed: 124 additions & 93 deletions

File tree

include/bitcoin/system/chain/block.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,13 @@ class BC_API block
138138
code connect(const context& ctx) const NOEXCEPT;
139139
code confirm(const context& ctx) const NOEXCEPT;
140140

141-
/// Populate previous outputs (and metadata.locked) internal to the block.
142-
/// False if one or more populated prevouts is locked in the block context.
143-
bool populate(const context& ctx) const NOEXCEPT;
141+
/// Populate previous outputs internal to the block.
142+
void populate() const NOEXCEPT;
143+
144+
/// Populate previous outputs and metadata.locked internal to the block.
145+
/// Execution is shortcircuited for error with that metadata.locked set.
146+
/// False if any populated prevout is immature in the block context.
147+
code populate_with_metadata(const context& ctx) const NOEXCEPT;
144148

145149
protected:
146150
block(const chain::header::cptr& header,

include/bitcoin/system/chain/input.hpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class BC_API input
4444
typedef std::shared_ptr<const input> cptr;
4545

4646
static bool is_relative_locktime_applied(uint32_t sequence) NOEXCEPT;
47-
static bool is_locked(uint32_t sequence, size_t height,
47+
static bool is_relative_locked(uint32_t sequence, size_t height,
4848
uint32_t median_time_past, size_t prevout_height,
4949
uint32_t prevout_median_time_past) NOEXCEPT;
5050

@@ -117,17 +117,14 @@ class BC_API input
117117
size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT;
118118

119119
/// Requires metadata.height and median_time_past (otherwise returns true).
120-
bool is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT;
120+
bool is_relative_locked(size_t height,
121+
uint32_t median_time_past) const NOEXCEPT;
121122

122123
protected:
123124
input(const chain::point::cptr& point, const chain::script::cptr& script,
124125
const chain::witness::cptr& witness, uint32_t sequence,
125126
bool valid) NOEXCEPT;
126127

127-
/// Any non-zero relative locktime value locks internally-spent input.
128-
friend class transaction;
129-
bool is_internal_lock() const NOEXCEPT;
130-
131128
private:
132129
typedef struct { size_t nominal; size_t witnessed; } sizes;
133130

include/bitcoin/system/chain/prevout.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class BC_API prevout final
5454
uint32_t median_time_past{ max_uint32 };
5555

5656
/// node: set to the database record of the input association.
57+
/// node: it is necessary that Link::terminal derives from max_uint32.
5758
uint32_t link;
5859
};
5960

include/bitcoin/system/chain/transaction.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class BC_API transaction
134134
bool is_dusty(uint64_t minimum_output_value) const NOEXCEPT;
135135

136136
/// Requires no metadata, true if spend in own block would be locked.
137-
bool is_internal_lock(const input& in) const NOEXCEPT;
137+
bool is_internally_locked(const input& in) const NOEXCEPT;
138138

139139
/// Assumes coinbase if prevout not populated (returns only legacy sigops).
140140
size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT;
@@ -204,7 +204,7 @@ class BC_API transaction
204204
/// Check (requires context).
205205
/// -----------------------------------------------------------------------
206206

207-
bool is_non_final(size_t height, uint32_t timestamp,
207+
bool is_absolute_locked(size_t height, uint32_t timestamp,
208208
uint32_t median_time_past, bool bip113) const NOEXCEPT;
209209

210210
/// Accept (requires prevouts).
@@ -220,7 +220,8 @@ class BC_API transaction
220220
/// -----------------------------------------------------------------------
221221

222222
/// Requires input.metadata.height/median_time_past (prevout confirmation).
223-
bool is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT;
223+
bool is_relative_locked(size_t height,
224+
uint32_t median_time_past) const NOEXCEPT;
224225

225226
/// Requires input.metadata.height (prevout confirmation).
226227
bool is_immature(size_t height) const NOEXCEPT;

include/bitcoin/system/error/transaction_error_t.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ enum transaction_error_t
4747

4848
// accept transaction
4949
unexpected_witness_transaction,
50-
transaction_non_final,
5150
premature_validation,
5251
unspent_duplicate,
5352
missing_previous_output,
5453
double_spend,
5554
coinbase_maturity,
5655
spend_exceeds_value,
5756
transaction_sigop_limit,
57+
absolute_time_locked,
5858
relative_time_locked,
5959
transaction_weight_limit,
6060

src/chain/block.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -710,10 +710,38 @@ bool block::is_unspent_coinbase_collision() const NOEXCEPT
710710
}
711711

712712
// Search is unordered, forward refs (and duplicates) caught by block.check.
713-
bool block::populate(const chain::context& ctx) const NOEXCEPT
713+
void block::populate() const NOEXCEPT
714714
{
715715
if (txs_->empty())
716-
return true;
716+
return;
717+
718+
unordered_map_of_cref_point_to_output_cptr_cref points{ outputs() };
719+
uint32_t index{};
720+
721+
// Populate outputs hash table (coinbase included).
722+
for (auto tx = txs_->begin(); tx != txs_->end(); ++tx, index = 0)
723+
for (const auto& out: *(*tx)->outputs_ptr())
724+
points.emplace(cref_point{ (*tx)->get_hash(false), index++ }, out);
725+
726+
// Populate input prevouts from hash table.
727+
for (auto tx = std::next(txs_->begin()); tx != txs_->end(); ++tx)
728+
{
729+
for (const auto& in: *(*tx)->inputs_ptr())
730+
{
731+
// Map chain::point to cref_point for search, should optimize away.
732+
const auto point = points.find({ in->point().hash(),
733+
in->point().index() });
734+
735+
if (point != points.end())
736+
in->prevout = point->second;
737+
}
738+
}
739+
}
740+
741+
code block::populate_with_metadata(const chain::context& ctx) const NOEXCEPT
742+
{
743+
if (txs_->empty())
744+
return error::block_success;
717745

718746
const auto bip68 = ctx.is_enabled(chain::flags::bip68_rule);
719747
unordered_map_of_cref_point_to_output_cptr_cref points{ outputs() };
@@ -724,8 +752,7 @@ bool block::populate(const chain::context& ctx) const NOEXCEPT
724752
for (const auto& out: *(*tx)->outputs_ptr())
725753
points.emplace(cref_point{ (*tx)->get_hash(false), index++ }, out);
726754

727-
// Populate input prevouts from hash table and obtain locked state.
728-
auto locked = false;
755+
// Populate input prevouts from hash table and obtain maturity.
729756
for (auto tx = std::next(txs_->begin()); tx != txs_->end(); ++tx)
730757
{
731758
for (const auto& in: *(*tx)->inputs_ptr())
@@ -736,19 +763,23 @@ bool block::populate(const chain::context& ctx) const NOEXCEPT
736763

737764
if (point != points.end())
738765
{
739-
// Zero maturity coinbase spend is treated as locked.
740-
const auto lock = (bip68 && (*tx)->is_internal_lock(*in));
766+
// Zero maturity coinbase spend is immature.
767+
const auto lock = (bip68 && (*tx)->is_internally_locked(*in));
741768
const auto immature = !is_zero(coinbase_maturity) &&
742769
(in->point().hash() == txs_->front()->get_hash(false));
743770

744771
in->prevout = point->second;
745-
in->metadata.locked = immature || lock;
746-
locked |= in->metadata.locked;
772+
if ((in->metadata.locked = (immature || lock)))
773+
{
774+
// Shortcircuit population and return above error.
775+
return immature ? error::coinbase_maturity :
776+
error::relative_time_locked;
777+
}
747778
}
748779
}
749780
}
750781

751-
return !locked;
782+
return error::block_success;
752783
}
753784

754785
// Delegated.

src/chain/input.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ bool input::is_relative_locktime_applied(uint32_t sequence) NOEXCEPT
399399
}
400400

401401
// static
402-
bool input::is_locked(uint32_t sequence, size_t height,
402+
bool input::is_relative_locked(uint32_t sequence, size_t height,
403403
uint32_t median_time_past, size_t prevout_height,
404404
uint32_t prevout_median_time_past) NOEXCEPT
405405
{
@@ -412,29 +412,26 @@ bool input::is_locked(uint32_t sequence, size_t height,
412412
// BIP68: bit 22 determines if relative lock is time or block based.
413413
if (get_right(sequence, relative_locktime_time_locked_bit))
414414
{
415+
// BIP68: references to median time past are as defined by bip113.
415416
// BIP68: change sequence to seconds by shift up by 9 bits (x 512).
416417
auto time = shift_left(blocks, relative_locktime_seconds_shift_left);
417418
auto age = floored_subtract(median_time_past, prevout_median_time_past);
418419
return age < time;
419420
}
420421

422+
// BIP68: when the relative lock time is block based, it is interpreted as
423+
// a minimum block height constraint over the age of the input.
421424
const auto age = floored_subtract(height, prevout_height);
422425
return age < blocks;
423426
}
424427

425-
bool input::is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT
428+
bool input::is_relative_locked(size_t height,
429+
uint32_t median_time_past) const NOEXCEPT
426430
{
427431
// Prevout must be found and height/median_time_past metadata populated.
428432
////BC_ASSERT(!is_zero(metadata.height));
429-
return is_locked(sequence_, height, median_time_past, metadata.height,
430-
metadata.median_time_past);
431-
}
432-
433-
// protected (tx friend)
434-
bool input::is_internal_lock() const NOEXCEPT
435-
{
436-
// Internal spends have no relative height/mtp (any metadata values work).
437-
return is_locked(metadata.height, metadata.median_time_past);
433+
return is_relative_locked(sequence_, height, median_time_past,
434+
metadata.height, metadata.median_time_past);
438435
}
439436

440437
bool input::reserved_hash(hash_digest& out) const NOEXCEPT

src/chain/transaction.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ bool transaction::is_invalid_coinbase_size() const NOEXCEPT
10471047
// Accept (contextual).
10481048
// ----------------------------------------------------------------------------
10491049

1050-
bool transaction::is_non_final(size_t height, uint32_t timestamp,
1050+
bool transaction::is_absolute_locked(size_t height, uint32_t timestamp,
10511051
uint32_t median_time_past, bool bip113) const NOEXCEPT
10521052
{
10531053
// BIP113: comparing the locktime against the median of the past 11 block
@@ -1153,7 +1153,7 @@ bool transaction::is_relative_locktime_applied(bool coinbase, uint32_t version,
11531153
(version >= relative_locktime_min_version);
11541154
}
11551155

1156-
bool transaction::is_internal_lock(const input& in) const NOEXCEPT
1156+
bool transaction::is_internally_locked(const input& in) const NOEXCEPT
11571157
{
11581158
// BIP68: not applied to the sequence of the input of a coinbase.
11591159
BC_ASSERT(!is_coinbase());
@@ -1162,10 +1162,12 @@ bool transaction::is_internal_lock(const input& in) const NOEXCEPT
11621162
if (version_ < relative_locktime_min_version)
11631163
return false;
11641164

1165-
return in.is_internal_lock();
1165+
// Internal spends have no relative height/mtp (own metadata vs. itself).
1166+
return in.is_relative_locked(in.metadata.height,
1167+
in.metadata.median_time_past);
11661168
}
11671169

1168-
bool transaction::is_locked(size_t height,
1170+
bool transaction::is_relative_locked(size_t height,
11691171
uint32_t median_time_past) const NOEXCEPT
11701172
{
11711173
// BIP68: not applied to the sequence of the input of a coinbase.
@@ -1178,11 +1180,9 @@ bool transaction::is_locked(size_t height,
11781180
// BIP68: references to median time past are as defined by bip113.
11791181
const auto locked = [=](const auto& input) NOEXCEPT
11801182
{
1181-
return input->is_locked(height, median_time_past);
1183+
return input->is_relative_locked(height, median_time_past);
11821184
};
11831185

1184-
// BIP68: when the relative lock time is block based, it is interpreted as
1185-
// a minimum block height constraint over the age of the input.
11861186
return std::any_of(inputs_->begin(), inputs_->end(), locked);
11871187
}
11881188

@@ -1289,8 +1289,8 @@ code transaction::check(const context& ctx) const NOEXCEPT
12891289
{
12901290
const auto bip113 = ctx.is_enabled(bip113_rule);
12911291

1292-
if (is_non_final(ctx.height, ctx.timestamp, ctx.median_time_past, bip113))
1293-
return error::transaction_non_final;
1292+
if (is_absolute_locked(ctx.height, ctx.timestamp, ctx.median_time_past, bip113))
1293+
return error::absolute_time_locked;
12941294

12951295
return error::transaction_success;
12961296
}
@@ -1325,7 +1325,7 @@ code transaction::confirm(const context& ctx) const NOEXCEPT
13251325

13261326
if (is_coinbase())
13271327
return error::transaction_success;
1328-
if (bip68 && is_locked(ctx.height, ctx.median_time_past))
1328+
if (bip68 && is_relative_locked(ctx.height, ctx.median_time_past))
13291329
return error::relative_time_locked;
13301330
if (is_immature(ctx.height))
13311331
return error::coinbase_maturity;

src/error/transaction_error_t.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ DEFINE_ERROR_T_MESSAGE_MAP(transaction_error)
4141

4242
// accept transaction
4343
{ unexpected_witness_transaction, "unexpected witness transaction" },
44-
{ transaction_non_final, "transaction currently non-final" },
4544
{ premature_validation, "transaction validation under checkpoint" },
4645
{ unspent_duplicate, "matching transaction with unspent outputs" },
4746
{ missing_previous_output, "previous output not found" },
4847
{ double_spend, "double spend of input" },
4948
{ coinbase_maturity, "immature coinbase spent" },
5049
{ spend_exceeds_value, "spend exceeds value of inputs" },
5150
{ transaction_sigop_limit, "too many transaction embedded signature operations" },
52-
{ relative_time_locked, "transaction currently locked" },
51+
{ absolute_time_locked, "transaction absolute time locked" },
52+
{ relative_time_locked, "transaction relative time locked" },
5353
{ transaction_weight_limit, "transaction weight limit exceeded" },
5454

5555
// dconfirm transaction

0 commit comments

Comments
 (0)