Skip to content

Fix locktime calculations and improve API#36

Merged
ValuedMammal merged 2 commits intobitcoindevkit:masterfrom
evanlinjin:feature/optional-mtp
Feb 13, 2026
Merged

Fix locktime calculations and improve API#36
ValuedMammal merged 2 commits intobitcoindevkit:masterfrom
evanlinjin:feature/optional-mtp

Conversation

@evanlinjin
Copy link
Member

@evanlinjin evanlinjin commented Jan 18, 2026

Description

Previously, time-based locktimes are based on header timestamps, but they should be based on MTP.

Changes

  • Correctly calculate spendability of coins that are time-locked by taking MTP into account.
  • Rename TxStatus to ConfirmationStatus.
  • Split is_timelocked method into two methods as time-based and block-based timelocks have different required metadata.

Testing

Testing is done in #38 as they require some unreleased changes in upstream crates.

@evanlinjin evanlinjin marked this pull request as draft January 20, 2026 08:56
@evanlinjin evanlinjin force-pushed the feature/optional-mtp branch 5 times, most recently from a10729c to 9f83bf8 Compare January 25, 2026 10:01
@oleonardolima oleonardolima self-requested a review January 31, 2026 01:04
@evanlinjin evanlinjin force-pushed the feature/optional-mtp branch 4 times, most recently from 174c73f to bdc7fcf Compare February 4, 2026 10:01
@evanlinjin evanlinjin changed the title WIP: Fix locktime calculations and improve API Fix locktime calculations and improve API Feb 4, 2026
@evanlinjin evanlinjin marked this pull request as ready for review February 4, 2026 10:22
Copy link
Collaborator

@aagbotemi aagbotemi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this PR @evanlinjin. I left some review

src/input.rs Outdated
Comment on lines +374 to +378
if let (Some(relative::LockTime::Blocks(lt_height)), Some(conf_status)) =
(self.plan.relative_timelock(), self.status)
{
let height_diff = spending_height.saturating_sub(conf_status.height.to_consensus_u32());
return lt_height.to_consensus_u32() > height_diff;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When status is None (unconfirmed), this returns false, incorrectly marking the input as not timelocked. Should return true when status.is_none() && relative_lock.is_some().

src/input.rs Outdated
Comment on lines +394 to +403
if let (Some(relative::LockTime::Time(lt_time)), Some(conf_status)) =
(self.plan.relative_timelock(), self.status)
{
let time_diff = tip_mtp
.to_consensus_u32()
.saturating_sub(conf_status.prev_mtp?.to_consensus_u32());
return Some(lt_time.value() as u32 * 512 > time_diff);
}

Some(false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When status is None (unconfirmed), this returns Some(false), incorrectly marking the input as not timelocked. Unconfirmed inputs with relative time locks should return Some(true).

@evanlinjin evanlinjin force-pushed the feature/optional-mtp branch from bdc7fcf to 85b311f Compare February 6, 2026 08:01
evanlinjin added a commit to evanlinjin/bdk-tx that referenced this pull request Feb 6, 2026
Add comprehensive unit tests covering edge cases for is_block_timelocked,
is_time_timelocked, and is_timelocked methods:

- Unconfirmed inputs with relative timelocks (must be considered locked)
- Missing prev_mtp for relative time locks (returns None)
- No-lock and wrong-lock-type scenarios (return false/Some(false))
- Combined is_timelocked method with various lock combinations

These tests cover the bug cases identified in PR bitcoindevkit#36 review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@evanlinjin
Copy link
Member Author

@ValuedMammal @aagbotemi Thank you for the reviews. Tests in #38 have also been updated to cover these edge cases.

Copy link
Collaborator

@ValuedMammal ValuedMammal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 85b311f

src/input.rs Outdated

/// Whether this is locked by any timelock constraint.
///
/// Returns `None` if a time-based lock exists but `spending_mtp` is not provided or
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📌 "spending_mtp" doesn't appear in the function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines +321 to +322
spend_height: absolute::Height,
spend_mtp: Option<absolute::Time>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📌 Should it be spend_height or tip_height? Also consider rename to just filter_unspendable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Addressed both.

Copy link
Collaborator

@aagbotemi aagbotemi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 85b311f

Comment on lines +18 to +19
/// Previous block's MTP (median time past) value as per BIP-0068, if available.
pub prev_mtp: Option<absolute::Time>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the docs for prev_mtp don't explain what happens when it's None

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorted.

Correctly calculate spendability of coins that are timelocked by taking
MTP into account.

API changes are a result of the different requirements between relative
and absolute locktimes.
Copy link
Collaborator

@aagbotemi aagbotemi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK a35ecbf

@ValuedMammal
Copy link
Collaborator

ACK a35ecbf

@ValuedMammal ValuedMammal merged commit 338857f into bitcoindevkit:master Feb 13, 2026
6 checks passed
@ValuedMammal ValuedMammal mentioned this pull request Feb 17, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants