diff --git a/include/bitcoin/system/chain/input.hpp b/include/bitcoin/system/chain/input.hpp index 1afac27da1..00fb41cdca 100644 --- a/include/bitcoin/system/chain/input.hpp +++ b/include/bitcoin/system/chain/input.hpp @@ -118,14 +118,15 @@ class BC_API input /// Requires metadata.height and median_time_past (otherwise returns true). bool is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT; - /// Any non-zero relative locktime value locks internally-spent input. - bool is_internally_locked() const NOEXCEPT; - protected: input(const chain::point::cptr& point, const chain::script::cptr& script, const chain::witness::cptr& witness, uint32_t sequence, bool valid) NOEXCEPT; + /// Any non-zero relative locktime value locks internally-spent input. + friend class transaction; + bool is_internal_lock() const NOEXCEPT; + private: typedef struct { size_t nominal; size_t witnessed; } sizes; diff --git a/include/bitcoin/system/chain/transaction.hpp b/include/bitcoin/system/chain/transaction.hpp index 54d931a71f..5e129fb677 100644 --- a/include/bitcoin/system/chain/transaction.hpp +++ b/include/bitcoin/system/chain/transaction.hpp @@ -128,6 +128,9 @@ class BC_API transaction bool is_empty() const NOEXCEPT; bool is_dusty(uint64_t minimum_output_value) const NOEXCEPT; + /// Requires no metadata, true if spend in own block would be locked. + bool is_internal_lock(const input& in) const NOEXCEPT; + /// Assumes coinbase if prevout not populated (returns only legacy sigops). size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT; diff --git a/src/chain/block.cpp b/src/chain/block.cpp index 963c74d1c6..6806edfe1d 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -730,7 +730,7 @@ bool block::populate(const chain::context& ctx) const NOEXCEPT if (point != points.end()) { in->prevout = point->second; - in->metadata.locked = bip68 && in->is_internally_locked(); + in->metadata.locked = bip68 && (*tx)->is_internal_lock(*in); locked |= in->metadata.locked; } } diff --git a/src/chain/input.cpp b/src/chain/input.cpp index 6d77ee2948..71111c83c2 100644 --- a/src/chain/input.cpp +++ b/src/chain/input.cpp @@ -416,10 +416,11 @@ bool input::is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT metadata.median_time_past); } -bool input::is_internally_locked() const NOEXCEPT +// protected (tx friend) +bool input::is_internal_lock() const NOEXCEPT { - // Internal spends have zero relative height/mtp. - return is_locked(sequence_, {}, {}, {}, {}); + // Internal spends have no relative height/mtp (any metadata values work). + return is_locked(metadata.height, metadata.median_time_past); } bool input::reserved_hash(hash_digest& out) const NOEXCEPT diff --git a/src/chain/transaction.cpp b/src/chain/transaction.cpp index 13d341b624..f90f6ad928 100644 --- a/src/chain/transaction.cpp +++ b/src/chain/transaction.cpp @@ -1151,6 +1151,18 @@ bool transaction::is_immature(size_t height) const NOEXCEPT return !std::all_of(inputs_->begin(), inputs_->end(), mature); } +bool transaction::is_internal_lock(const input& in) const NOEXCEPT +{ + // BIP68: not applied to the sequence of the input of a coinbase. + BC_ASSERT(!is_coinbase()); + + // BIP68: applied to txs with a version greater than or equal to two. + if (version_ < relative_locktime_min_version) + return false; + + return in.is_internal_lock(); +} + bool transaction::is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT {