-
Notifications
You must be signed in to change notification settings - Fork 420
Add median-time-past (MTP) calculation to CheckPoint #2037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
f97553f to
0f75c22
Compare
oleonardolima
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cACK 0f75c22
I'll just give it a try with some other test scenarios before final ACK.
f97553f to
46e751f
Compare
crates/core/src/checkpoint.rs
Outdated
| pub fn median_time_past(&self) -> Option<u32> { | ||
| let current_height = self.height(); | ||
| let earliest_height = current_height.saturating_sub(Self::MTP_BLOCK_COUNT); | ||
| self._median_time_past(earliest_height..current_height) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious if these new APIs could output wrong/invalid values if the blocks inserted have a wrong/invalid time field e.g malicious block source.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely. We are trusting the chain source here (and everywhere in BDK). Eventually, we should think about how to verify all consensus rules in BDK: difficulty, PoW, MTP, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it not inclusive of the current height?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I want to avoid the temptation to resort to using these underscore methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it not inclusive of the current height?
This method is asking "what's the MTP that was used to validate block 100".
However, if the caller is asking "what MTP should I use to validate a transaction going into the next block", next_median_time_past() can be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I want to avoid the temptation to resort to using these underscore methods.
I renamed the private method to middle_timestamp_of_range.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it not inclusive of the current height?
This method is asking "what's the MTP that was used to validate block 100".
However, if the caller is asking "what MTP should I use to validate a transaction going into the next block",
next_median_time_past()can be used.
I imagine there would just be one function called median_time_past with a range that includes the current tip. If I wanted to know the last MTP, I would just call this function on the previous checkpoint, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wrong about the definition of MTP. MTP of block h includes the time of block h. However, when validating whether a tx can be included into a block at height x, consensus uses MTP(x-1).
I imagine there would just be one function called
median_time_pastwith a range that includes the current tip.
Yes. This makes sense as MTP(spending_block - 1) is essentially MTP(tip).
If I wanted to know the last MTP, I would just call this function on the previous checkpoint, right?
This is true. However, a prev-MTP helper will be useful for relative-timelocked txs as we may need to get the confirmation block hash as well as MTP(conf_block - 1).
Edit: I agree with you. Less is more.
46e751f to
74d30d8
Compare
oleonardolima
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cACK 74d30d8
I didn't really find other test vectors that we could use.
Another thing that should probably be handled in a future PR is updating the calculation of Balance for the immature category, we probably should update it to consider transactions that are not yet mature considering the Locktime and MTP for it.
crates/core/src/checkpoint.rs
Outdated
| fn block_time(&self) -> Option<u32> { | ||
| None | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm but then the trait would need to be called ToBlockHashAndHeaderTime? Maybe just have a new impl where D is convertible to a Header (granted, the time is only a subset of the information contained in a header).
impl<D> CheckPoint<D>
where
D: Into<Header>,
{
/// Calculates the median time past (MTP) of this `CheckPoint`.
pub fn median_time_past(&self) -> Option<u32> { ... }
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about a separate ToBlockTime trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes or maybe stay with 1 trait but redefine ToBlockHash to be a more general Header-like interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think 2 traits is nicer otherwise the to_blocktime method needs to return an Option.
crates/core/src/checkpoint.rs
Outdated
| pub fn median_time_past(&self) -> Option<u32> { | ||
| let current_height = self.height(); | ||
| let earliest_height = current_height.saturating_sub(Self::MTP_BLOCK_COUNT); | ||
| self._median_time_past(earliest_height..current_height) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I want to avoid the temptation to resort to using these underscore methods.
6704eec to
00b0ca8
Compare
luisschwab
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 00b0ca8
crates/core/src/checkpoint.rs
Outdated
| }) | ||
| .collect::<Option<Vec<u32>>>()?; | ||
| timestamps.sort_unstable(); | ||
| Some(timestamps[timestamps.len().checked_sub(1)? / 2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit: BIP-0113 specifies len / 2. In the case of 11 blocks, both len / 2 and (len - 1)/2 yield 5, which is fine, but for even lengths, we get two elements. The median of an even number of elements is the average of the middle elements. Here we must choose one of the values, so perhaps remove the .checked_sub(1) to keep on par with the BIP?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arggggg
Add MTP calculation methods for CheckPoint following Bitcoin's BIP113: - `median_time_past()`: calculates MTP for current block using 11 blocks starting from the current block - Returns None when required blocks are missing Includes comprehensive tests for all edge cases.
00b0ca8 to
3d87a8c
Compare
|
FYI in |
Thanks! Unfortunately, I don't think we can use that here 😢 |
|
CI is failing |
Description
This PR adds the ability to calculate median-time-past (MTP) for
CheckPointstructures, implementing the functionality described in #2036.Notes to the reviewers
CheckPoint::median_time_pastcalculates the MTP value by looking at the previous 11 blocks (including the current block).Changelog notice
Added
ToBlockTimetrait for types that can return a block time.median_time_past()method toCheckPointfor calculating MTP according to BIP113Checklists
All Submissions:
cargo fmtandcargo clippybefore committingNew Features:
Fixes #2036