Expose valid (min, max) difficulty transition thresholds#1820
Expose valid (min, max) difficulty transition thresholds#1820apoelstra merged 1 commit intorust-bitcoin:masterfrom wpaulino:impl-shift-target
Conversation
tcharding
left a comment
There was a problem hiding this comment.
Thanks for the contribution! Sorry for rugging you :)
My review is just for code correctness, I have no knowledge on whether or not Target should expose these, the use case sounds sane to me though.
ACK 669f248b294868ee89423dd2be21bc4000530ca3
|
Isn't If not, I would still prefer having a method to do the verification rather than ad-hoc shr/shl because presumably more people will want to do this kind of verification, not just LDK. Also this shouldn't block you because you can implement it manually: // this is shl, shr is similar
let bytes = target.to_be_bytes();
let high = u128::from_be_bytes(bytes[..16].try_into().unwrap());
let low = u128::from_be_bytes(bytes[16..].try_into().unwrap());
let transferred_bits = low >> (128 - bits_to_shift);
let high = high << bits_to_shift | transferred_bits;
let low = low << bits_to_shift;
let mut shifted_bytes = [0u8; 64];
shifted_bytes[..16].copy_from_slice(&high.to_be_bytes());
shifted_bytes[16..].copy_from_slice(&high.to_le_bytes());
Target::from_le_bytes(shifted_bytes)I know it's not nice but at least it's not impossible and we can clean it up soon. |
|
@Kixunil I think the shift operators make sense here, since they shift "how many bits of difficulty" the target is. I'm leaning toward ACKing this because the operators do make conceptual sense, and they don't make sense for |
|
But yes, the specific "determine difficulty rails after an adjustment" algorithm that @wpaulino is describing is something we should have first-class support for! Note that the difficulty adjustment is bounded by "divide by 4" below and "multiply by 4" above. So this is a nearly trivial operation even if your only operators are shifts. It would be a shame to turn 2 lines into 20. Edit It's actually a little more subtle, I remembered, since there's a hard upper bound at difficulty 1. We probably should provide such a method (and maybe one for |
apoelstra
left a comment
There was a problem hiding this comment.
ACK 669f248b294868ee89423dd2be21bc4000530ca3
|
Isn't it a bit awkward to have only shift exposed and not other operations? Also can't find it now but IIRC division was also needed to adjust difficulty. One thing that bothers me about this is that lowering target by an amount that can not be expressed as shift is still valid operation and does increase difficulty but we don't allow it. |
Fair enough. I figured since it was something already supported by the API previously, and it's required for a valid use-case, it would make sense to expose it again.
Yeah, I thought so too. I guess it depends on what plans you all have for |
|
I'd also strongly prefer if rust-bitcoin had a method for this, but the context-less validate_pow isn't what we need - we want to verify that a difficulty adjustment is sane. This is a really critical operation to prevent all kinds of DoS attacks on clients which download headers, and while it would be great if we could call out to rust-bitcoin and have it tell us if a difficulty transition given a pile of headers is correct, just checking the 4x bounds at least addresses the DoS issues for the most part. |
|
I think we should expose both functions (compute min/max bounds, and compute exact adjustment). And we shouldn't expose the shifts. @wpaulino could you adjust this PR to expose a min/max function instead? (You could also implement a full difficulty calculation if you're feeling generous, but that'd be 10x the work and it sounds like you'd be satisfied with just the min/max for now.) |
|
@apoelstra only exposed the thresholds for now, but I may come back around to implement the exact adjustment computation in the future. |
|
Needs |
tcharding
left a comment
There was a problem hiding this comment.
ACK 6187983f7b89647c53fa42f6c752c169bd3d2837
|
Thanks man! |
|
@wpaulino the |
|
Whoops, thanks. Also bounded the |
bitcoin/src/pow.rs
Outdated
There was a problem hiding this comment.
I'm not a fan of this, it reads funnily. If we are not upholding the invariant that Self is less than or equal to Self::MAX then we should fix that and not band aid over it here.
There was a problem hiding this comment.
Is this something we want to block this PR on, or can it come after this lands?
I also realized that MAX here is network dependent. The current value is valid for mainnet and testnet, but regtest and signet have higher values, invalidating the current MAX constant. This makes me think that until we have network dependent max values, we shouldn't do any bounds checking, unless we only plan to support mainnet for such operations.
There was a problem hiding this comment.
o.O you can have difficulty < 1 for regtest and signet? What is the corresponding target value?
There was a problem hiding this comment.
Or do you mean that the conversion between difficulty and target is different on these networks....which would require we modify our conversion functions.
There was a problem hiding this comment.
The conversion is the same, but the highest target/PoW limit varies by network. IIUC, the difficulty is just the ratio between the highest target and the current target, so a difficulty of 1 just represents the highest target for that given network. With that said, it seems Target::difficulty currently only works for mainnet and testnet values, since they both share the same max target.
There was a problem hiding this comment.
:( so the conversion is different for different networks.
That is really obscure and does not seem necessary. But ok, we'll find a way to deal with it.
There was a problem hiding this comment.
How would you like to proceed then? Since we're not doing any bounds checking at all, and it depends on the active network, we could defer that to future work and not hold back the changes proposed here?
There was a problem hiding this comment.
Agree let's not hold this up.
Kixunil
left a comment
There was a problem hiding this comment.
I think the overflow should be fixed unless I'm missing something.
Once `U256` was made private, we lost the ability to check whether a valid difficulty transition was made in the chain, since `Target` no longer exposes any arithmetic operations.
| /// | ||
| /// The difficulty can only decrease or increase by a factor of 4 max on each difficulty | ||
| /// adjustment period. | ||
| pub fn max_difficulty_transition_threshold(&self) -> Self { Self(self.0 << 2) } |
There was a problem hiding this comment.
This will debug-panic on large values, is this OK/correct?
There was a problem hiding this comment.
What makes you say that @Kixunil ? We use wrapping_shl to implement << so I thought this couldn't panic, what am I missing?
@wpaulino why did the check against MAX get removed here? In the previous iteration I only meant to comment that I didn't like the check in the min_difficulty_transition_threshold method, in the max one I thought it was correct.
There was a problem hiding this comment.
why did the check against MAX get removed here? In the previous iteration I only meant to comment that I didn't like the check in the min_difficulty_transition_threshold method, in the max one I thought it was correct.
Because MAX currently is only applicable to mainnet and testnet. I guess that's fine since this is also the case with Target::difficulty, but I figured since we want this to be network agnostic at some point, it doesn't make sense to bound by MAX until we have the network specific max constants.
There was a problem hiding this comment.
Oh yes, sorry, my bad for not remembering all the context when reviewing. I did read that before.
There was a problem hiding this comment.
Oh, sorry I got confused, it'd only panic if the argument was too large, 2 is not too large.
There was a problem hiding this comment.
I constantly have this same confusion about left-shift. It's a little weird, this notion of "overflow" is different from that for the arithmetic opcodes.
Retracting my ACK. I might need some more time to check this completely. bitcoin core does these operations of in64_t. and then converts to uint256_t. Need to make sure if the way we are doing it is correct
@sanket1729 could you point to what you were looking at? There should only be a conversion from the compact representation ( |
sanket1729
left a comment
There was a problem hiding this comment.
ACK 8e6f953 . Sorry, was confused by some details.
|
Implementors of difficulty transition algorithms should take care to only operate on Some of these conversions are lossy, and could lead to subtle bugs. |
|
I'm happy to merge this as-is. We don't expose enough functionality yet for somebody to compute actual difficulty targets so I'm not too worried about somebody using the incorrect-on-extreme-values logic to cause consensus faults (also, even if you did, the first 10000 or so Bitcoin blocks hit the min-difficulty clamp so this would be immediately visible). And as written, this function serves well as a simple anti-DoS sanity check on a provided chain. Interesting that |
|
Have I gone mad, where are these changes gone? They are not on master anymore.
cc @apoelstra |
|
ugh, I am a total wombat, I had an old tag checked out from yesterday. Face palm. |
We just backported PR rust-bitcoin#1820, add a changelog entry and bump the crate version.
ba99aa4 Bump version to 0.30.2 (Tobin C. Harding) 3c9bbca Expose valid (min, max) difficulty transition thresholds (Wilmer Paulino) Pull request description: Patch one backports #1820, I believe this is all that is needed for LDK to be able to upgrade to `rust-bitcoin v0.30`. Context: - #1820 - lightningdevkit/rust-lightning#2124 Patch 2 adds a changelog entry and bumps the minor version. ACKs for top commit: Kixunil: ACK ba99aa4 Tree-SHA512: 93adb52bbc8e61ece63e5653dea7cf6d2d25028f25cb8bbda14606d9ebf74becaee0a0c4f4f3d7fde56c0e2b8a57f8c9be263e04833161a1da0baaf8fcca8764
Once
U256was made private, we lost the ability to check whether a valid difficulty transition was made in the chain, sinceTargetdoesn't expose any operations. We only choose to exposeShl<u32>andShr<u32>such that we can compute the min and max target thresholds allowed for a difficulty transition.This is something we realized was missing after bumping to
rust-bitcoin v0.30.0inrust-lightning, specifically for ourlightning-block-synccrate. It may also be worth having a helper inrust-bitcointhat checks a header properly builds upon the previous, but that can be left for future work.