util: Type-safe transaction identifiers#28107
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
|
How is this different from |
I think mostly compile-time vs. runtime checks? (I guess with this PR we could consider making |
stickies-v
left a comment
There was a problem hiding this comment.
Concept ACK.
We don't consistently have clear variable naming (e.g. the ambiguous hash; or txid being used to refer to a wtxid), sometimes requiring quite a bit of developer time to figure out the expected ~type. I have experienced this quite a bit myself.
Introducing types makes this explicit at relatively low overhead, and I think the approach of subclassing uint256 makes sense.
We'll have to be careful for this not to lead to unnecessary refactoring code churn, but that shouldn't be a reason not to make this change imo.
|
I've definitely written/seen bugs that could be prevented if Txid and Wtxid were different types. When I review code it's something that I look for and it comes up surprisingly often. Interfaces taking Agree that checking at compile time would be nice and probably prevent bugs / make review easier. Concept ACK assuming we're ok with the amount of code change. |
In terms of refactoring, this should be similar to the time type-safety PRs in that we can gradually convert our code. |
|
concept ACK, it comes up pretty often to work around this I'm writing |
|
If helpful, a somewhat similar pros/cons discussion took place in March 2021 in #21328. |
eb26626 to
369e790
Compare
369e790 to
1aecdbc
Compare
1aecdbc to
c7338d8
Compare
|
Ready for review |
c7338d8 to
76fe2c3
Compare
|
Concept ACK |
|
Concept ACK, but I'm not too sure about the |
Could you clarify your objections about the |
instagibbs
left a comment
There was a problem hiding this comment.
ACK 76fe2c304201646d63b1b01b397e18a99252a2d9
played around with some extensions to see where it could go, I think longer term we could use GenTxid for situations where both are acceptable, and Wtxid/Txid when only one type is, and slowly migrate towards that. f.e. GenTxid could have a GetTypedHash to convert to the other.
|
@MarcoFalke could you give this another look? You've done similar work on our time type-safety, so I'd appreciate your feedback. |
76fe2c3 to
c1f9c3b
Compare
| constexpr int Compare(const Other& other) const | ||
| { | ||
| static_assert(ALWAYS_FALSE<Other>, "Forbidden comparison type"); | ||
| return 0; |
There was a problem hiding this comment.
I wonder should we use the [[noreturn]] attribute instead?
|
A type safety implies that the following code: CMutableTransaction tx;
uint256 hash = tx.GetHash();must fail to compile. But it does. If it is expected, maybe document such behaviour? |
It is documented here: bitcoin/src/util/transaction_identifier.h Lines 52 to 59 in 940a499 |
There was a problem hiding this comment.
ACK 940a499, I have reviewed the code and it looks OK.
nit in 940a499: using auto instead of explicit iterator types might make the code more concise, readable and maintainable.
EDIT: I saw #28107 (comment)
glozow
left a comment
There was a problem hiding this comment.
reACK 940a499
range-diff from last review
- export Txid and Wtxid #28107 (comment)
- no reassignment of inv #28107 (comment)
- comment telling people to use
FromUint256#28107 (comment) - overloaded instead of templated
Compare#28107 (comment) - removing unnecessary autoing #28107 (comment)
- includes
- dropping unnecessary #28107 (comment)
- using
std::byteinstead ofunsigned char+ needed casts #28107 (comment)
|
Assuming we should wait until branch off to merge? |
Any particular reason? If these start getting used more widely, it's just going to make doing backports more complicated if so. |
Idc that much, was asked why I didn't merge yet. Conversions are still possible so it shouldn't be that complicated, but whatever you prefer. Could use this time to look at what it looks like to change the rest of the codebase and see if we run into any bumps. |
| for (const auto& ptx : pblock->vtx) { | ||
| m_recent_confirmed_transactions.insert(ptx->GetHash()); | ||
| if (ptx->GetHash() != ptx->GetWitnessHash()) { | ||
| if (ptx->HasWitness()) { |
There was a problem hiding this comment.
Is this an improvement? Comparing two uint256s is O(1) time and fairly cache friendly, whereas checking an arbitrary number of inputs' witnesses might not be either of those.
There was a problem hiding this comment.
Comparing Txid and Wtxid is no longer allowed. We could use ToUint256 or add a cache for HasWitness?
I like using HasWitness because it is immediately obvious what we are checking.
There was a problem hiding this comment.
Can CTransaction::HasWitness be changed to check whether hash != m_witness_hash (after the unavoidable first time when you need to iterate through the inputs to look for witnesses)?
There was a problem hiding this comment.
Could move the "iterate through" part into ComputeWitnessHash, and have HasWitness just return hash.ToUint256() == m_witness_hash.ToUint256(). Note that that's an example of where ToUint256() not doing a copy would make sense.
| uint256 m_wrapped; | ||
|
|
||
| // Note: Use FromUint256 externally instead. | ||
| transaction_identifier(const uint256& wrapped) : m_wrapped{wrapped} {} |
There was a problem hiding this comment.
What's the advantage of FromUint256 over just declaring this as explicit and making it public?
There was a problem hiding this comment.
That might be better but would allow something like Txid{Wtxid{}} right now (because of the conversion function). We could make it a template like we do for Compare...
There was a problem hiding this comment.
But (a) that would presumably be silly to write, and (b) the conversion function is going away, which will ensure any code like that also goes away.
|
ACK 940a499 |
|
I'll open a follow up PR for @ajtowns's suggestions soon. |
We currently have two different identifiers for transactions:
txid(refering to the hash of a transaction without witness data) andwtxid(referring to the hash of a transaction including witness data). Both are typed asuint256which could lead to type-safety bugs in which one transaction identifier type is passed where the other would be expected.This PR introduces explicit
TxidandWtxidtypes that (if used) would cause compilation errors for such type confusion bugs.(Only the orphanage is converted to use these types in this PR)