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
Address validity invariant cleanups #1564
Conversation
1cad205
to
cc5ee00
Compare
} | ||
|
||
/// Returns a byte slice of the payload | ||
pub fn as_bytes(&self) -> &[u8] { | ||
match self { | ||
Payload::ScriptHash(hash) => hash.as_ref(), | ||
Payload::PubkeyHash(hash) => hash.as_ref(), | ||
Payload::WitnessProgram { program, .. } => program, | ||
Payload::WitnessProgram(prog) => prog.program(), |
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.
Unrelated: But this API is sketchy in how it is used. If anything I would expect the witness_version
to get serialized in as_bytes
method of the PayLoad
.
Should open an issue about it.
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.
Oh, I think I complained about this, IIRC this was introduced when implementing is_related_to_pubkey
. The function shouldn't be pub
.
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.
Looks good, I only want a reason to keep is_spend_standard
and if yes, then better doc.
program: Vec<u8>, | ||
}, | ||
WitnessProgram(WitnessProgram), | ||
} |
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.
Oh, I think this enum should've been [non_exhaustive]
. Anyway, I like this approach.
} | ||
|
||
/// Returns a byte slice of the payload | ||
pub fn as_bytes(&self) -> &[u8] { | ||
match self { | ||
Payload::ScriptHash(hash) => hash.as_ref(), | ||
Payload::PubkeyHash(hash) => hash.as_ref(), | ||
Payload::WitnessProgram { program, .. } => program, | ||
Payload::WitnessProgram(prog) => prog.program(), |
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.
Oh, I think I complained about this, IIRC this was introduced when implementing is_related_to_pubkey
. The function shouldn't be pub
.
/// | ||
// Used internally by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and `new_v1_p2tr_tweaked` | ||
// for convenience. | ||
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self { |
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.
Maybe add a debug_assert!
?
/// | ||
/// SegWit addresses with unassigned witness versions or non-standard program sizes are | ||
/// considered non-standard. | ||
pub fn is_spend_standard(&self) -> bool { self.address_type().is_some() } |
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.
If I'm correct that receiver statically knows the address is standard then I think the method is useless and we should just remove it. If there is any use then I want a warning in doc that this should not be called by the sender.
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.
If I'm correct that receiver statically knows the address is standard
This method is preciously how the receiver would know that the address they are receiving the money to is standard.
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.
The receiver is the one who constructs the address so I would expect this to be handled by construction.
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.
Are you suggesting two structures? SendingAddress
and RecievingAddress
. Or perhaps yet another phantom data type in Address itself. We want to support sending to unknown addresses, but don't want to receive it ourselves. I don't see how we can handle this under the current Address
construction.
That being said, I don't see any utility for wallet developers who want to use this method. It maybe useful enthusiasts who scan the blockchain for non-standard spends or for block explorers. I recall somebody did a taproot spend before taproot was activated to fork off UASF nodes.
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.
Are you suggesting two structures?
Not really. If I write an application that uses some of the common ways of receiving then I write the code such that Address
is always valid and don't need to check it. Would would I even do if it's wrong?
That being said, I don't see any utility for wallet developers who want to use this method.
Yeah, I believe we both don't for the same reason.
It maybe useful enthusiasts who scan the blockchain for non-standard spends or for block explorers.
Shouldn't the scripts be checked instead of addresses?
I recall somebody did a taproot spend before taproot was activated to fork off UASF nodes.
OT but sounds like that person was confused? I don't see how it could fork off UASF nodes.
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.
Not really. If I write an application that uses some of the common ways of receiving then I write the code such that Address is always valid and don't need to check it.
This is why it's a utility method and not something that we're gating constructors on or enforcing in the type system.
Would would I even do if it's wrong?
Lol assert probably. But I think it's most common to hit cases like this during transition periods...like, maybe your wallet would let you generate a Taproot address before Taproot was active, but would put a bunch of warnings on it or otherwise try to prevent you from using it until it became standard.
Shouldn't the scripts be checked instead of addresses?
This is an interesting thought. Maybe users who want to do this should need to first convert the address to a scriptpubkey, and then do a standardness check on that. But I think such an API change should wait for script tagging.
727456c
to
30d251e
Compare
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 30d251e
bitcoin/src/address.rs
Outdated
/// Returns a byte slice of the payload | ||
pub fn as_bytes(&self) -> &[u8] { | ||
/// Returns a byte slice of the inner program of the payload. Incase the payload | ||
/// ia a script hash or pubkey hash, the a reference to the hash is returned. |
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.
Nits: "is", "a reference"
bitcoin/src/address.rs
Outdated
20 => Some(AddressType::P2wpkh), | ||
32 => Some(AddressType::P2wsh), | ||
_ => None, | ||
_ => None, // Technically unreachable, but we don't want to panic when not required. |
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.
Silently ignoring bugs is not great either, debug_assert
would be nice.
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.
Agreed. TBH I'd even prefer an unconditional unreachable!
panic.
bitcoin/src/address.rs
Outdated
/// Checks whether or not the address is following Bitcoin standardness rules when | ||
/// *spending* from this address. This method should *NOT* called by senders. For forward | ||
/// compatibility, the senders must send to any [`Address`]. Receivers can use this method to | ||
/// check whether or not they can spend from this address. |
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 like having a warning in summary but not sure if this long is appropriate. Maybe something like "Checks whether the address obeys Bitcoin standardness rules when spending from this address. *NOT to be called by senders!" And full explanation in description. Anyway, I won't block the PR because of it.
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.
Changed
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.
Just a few rustdoc comment fixes.
bitcoin/src/address.rs
Outdated
/// Returns a byte slice of the inner program of the payload. Incase the payload | ||
/// ia a script hash or pubkey hash, the a reference to the hash is returned. |
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.
/// Returns a byte slice of the inner program of the payload. Incase the payload | |
/// ia a script hash or pubkey hash, the a reference to the hash is returned. | |
/// Returns a byte slice of the inner program of the payload. If the payload | |
/// is a script hash or pubkey hash, a reference to the hash is returned. |
bitcoin/src/blockdata/script.rs
Outdated
/// Internal method to a witness scriptPubkey with a given version and program. | ||
/// Does not do any checks on version or program. | ||
/// | ||
// Used internally by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and `new_v1_p2tr_tweaked` | ||
// for convenience. | ||
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self { |
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.
/// Internal method to a witness scriptPubkey with a given version and program. | |
/// Does not do any checks on version or program. | |
/// | |
// Used internally by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and `new_v1_p2tr_tweaked` | |
// for convenience. | |
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self { | |
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes. | |
/// | |
/// Convenience method used by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and | |
/// `new_v1_p2tr_tweaked`. | |
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self { |
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.
Addressed. But also added the Does not do any checks on version or program
which I feel is relevant to the method.
} | ||
|
||
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script. | ||
pub fn new_witness_program(version: WitnessVersion, program: &[u8]) -> Self { | ||
pub fn new_witness_program(witness_program: &WitnessProgram) -> Self { |
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.
pub fn new_witness_program(witness_program: &WitnessProgram) -> Self { | |
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`]. | |
pub fn new_witness_program(witness_program: &WitnessProgram) -> Self { |
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 30d251e
@sanket1729 should I merge this or do you want to address @tcharding's nits? |
@apoelstra, force-pushing in a few mins |
30d251e
to
b2f4559
Compare
Ready for review again.(after CI clears) |
b2f4559
to
f6f304e
Compare
Addresses with Segwitv0 not having len 20/32 are invalid and cannot be constructed. Also cleans up a API bug in ScriptBuf::new_witness_prog(ver, prog) allowing prog of invalid lenghts.
It is unlikely that we will see another segwit type fork. But we never know, best to mark Payload as [non-exhaustive]
Also make it private
f6f304e
to
44d3ec4
Compare
I have made another force push that adds details in the summary section of the doc. Looks really neat, I will use it more often in the future. |
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 44d3ec4
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 44d3ec4
Fixes #1561.
Highlights:
Address
struct. Such Addresses are useless and we should not parse/create them.is_standard
tois_spend_standard
.