Skip to content

Address type system#668

Closed
dr-orlovsky wants to merge 3 commits intorust-bitcoin:masterfrom
BP-WG:address/guarantees
Closed

Address type system#668
dr-orlovsky wants to merge 3 commits intorust-bitcoin:masterfrom
BP-WG:address/guarantees

Conversation

@dr-orlovsky
Copy link
Copy Markdown
Collaborator

@dr-orlovsky dr-orlovsky commented Sep 28, 2021

Major refactoring of witness program processing in address and scriptPubkeys. Makes address-related types to guarantee correctness of the witness programs.

Closes #648

@dr-orlovsky dr-orlovsky added the API break This PR requires a version bump for the next release label Sep 28, 2021
@dr-orlovsky dr-orlovsky added this to the 0.28.0 milestone Sep 28, 2021
Copy link
Copy Markdown
Collaborator

@Kixunil Kixunil left a comment

Choose a reason for hiding this comment

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

Concept ACK

@dr-orlovsky
Copy link
Copy Markdown
Collaborator Author

Did a big update completing this PR by providing guarantees for address-related type sanity

Copy link
Copy Markdown
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

EDIT: updated after the following discussion from #648 and reading rest of the commits.

Left a few comments on the first commit. Did not review all of it

My personal opinion is that this PR has too much code and additional data structures for the things it tries to achieve. I think developers using this library should only care about the following from address API:

  1. Users should be able to send to future addresses.
  2. Users should receive on known supported addresses.

To that end, I think we can do the following:

enum AddressInfo {
// we should send and receive to these addresses
Supported(AddressType),
// We should support sending to these, but don't receive here
Future(version, len),
}

This keeps the code diff small, users don't really need to know taprootv1 non-32 bytes, vs future version 2. If there is someone who is interested in implementing the differences of why something is valid vs non-valid, they can do so easily downstream. Also I don't see the point of SegwitInfo struct. There can be simple method to get Option<(verion, len)>

Curious what do other people feel about this?

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Oct 1, 2021

@sanket1729 I agree that detailed analysis has limited usefulness but there is at least one use case: in privacy-oriented protocols/wallets (e.g. BIP78) one needs to at least compare equality of address types to get maximum privacy. I think the current proposal goes beyond that but maybe @dr-orlovsky has some other use case in mind?

@dr-orlovsky
Copy link
Copy Markdown
Collaborator Author

@sanket1729 agree on replacing SegWitInfo with Option<(WitnessVersion, usize)>

@dr-orlovsky
Copy link
Copy Markdown
Collaborator Author

@Kixunil my usecase is professional software (wallets, blockchain explorers - Bitcoin Pro, MyCitadel) that provides as much detailed information as possible. Also databases indexing blockchain data for research purposes.

@apoelstra
Copy link
Copy Markdown
Member

I share Sanket's concerns that this PR adds a lot of data structures with unclear usecases, which will need to be adjusted in breaking ways in the future.

Wallets should be using rust-miniscript to keep track of their own addresses and related information. Block explorers etc. can use their own ad-hoc analysis of scriptPubKeys ... it is not at all obvious to me that they would want to categorize this information in the way proposed by this PR. And if they did, it is not obvious to me that this library is the place to add direct support.

@Kixunil
Copy link
Copy Markdown
Collaborator

Kixunil commented Nov 12, 2021

Yeah, I think having it in a separate crate with integrations would be better and closer to the idea of splitting up this crate.

@dr-orlovsky
Copy link
Copy Markdown
Collaborator Author

@sanket1729 @apoelstra @Kixunil

I have simplified this PR by removing FutureWitnessVersion and SegWitInfo data types. Also, I have addressed all comments and suggestions. The only thing that has left is two more variants in AddressInfo structure since they are needed to differentiate between valid recipient addresses and valid destination addresses (see https://github.com/rust-bitcoin/rust-bitcoin/pull/668/files#diff-c55d0186f3d5990b5e223c1a8aa1bbe60988201c452dd13b3324e892bc33119eR206-R236)

@sanket1729
Copy link
Copy Markdown
Member

Sorry for bringing this up again. In my opinion, there should not be any AddressInfo for NonSpendableV0. And NonTaprootV1 can be merged into Future enum. That would mean conversion from AddressType to AddressInfo should error instead of given it a variant of its own. Even though I have a strong preference here :) , I can go either way on NonSpendableV0.

At the very least, I think we should merge NonTaprootV1(which is just a bitcoin protocol implementation detail) into Future. I feel having only two types would make APIs really clean. AddressType::is_future, is_known. The value of having a simple API is a) serves good documentation and b) someone does not have to know all the inner details of bitcoin to work on a payment processor.

If you really want to analysis on script pubkeys, I would not be opposed to supporting it in rust-bitcoin. But I really think that could be a separate project.

Copy link
Copy Markdown
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

I think we should check if the 32-byte in p2tr is actually a correct x-only key in p2tr.


/// Returns whether support of the address will be possible only in a future through a softfork
#[inline]
pub fn requires_softfork(self) -> bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't know if this is useful but sure

Copy link
Copy Markdown
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

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

I'm not convinced this AddressInfo struct is helping folks out. I see the benefit of it for internals, having all the edge cases of the various variants there in code but for users coming to the library they are going to go Address -> AddressInfo -> WTF?

We already have a bunch of methods on Address that give useful hints about the address. Why not make AddressInfo private and just add methods as they are required?

Comment on lines +294 to +296
pub fn from_address_info(address_info: AddressInfo) -> Option<AddressType> {
address_info.address_type()
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we really need this method? It seem that a call such as let t = address_info.address_type() is just as erganomic, if not more so, than let t = AddressType::from_address_info(address_info). Or am I missing some other way of using this?

Comment on lines +208 to +209
/// NB: This does include future addresses which can be spent by anybody without a proper
/// signature before some future soft fork.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this comment correct? Seems that AddressInfo::Future returns false would imply that this does _not` include future addresses?

Comment on lines +224 to +225
/// by satisfying certain (address-specific) public key and/or script requirements and this may
/// not be changed with any future soft-fork.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this comment correct? Future returns true but comment says may not be changed with an future soft-fork?

}


/// Detects if the address can be used for security receiving funds, i.e. it can be spent only
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// Detects if the address can be used for security receiving funds, i.e. it can be spent only
/// Detects if the address can be used for securely receiving funds, i.e. it can be spent only

Comment on lines +253 to +254
/// this will be a non-Taproot witness v1 output, spendable by anyone under the current
/// consensus rules before one of future softfork would change that.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This sentence is worded a bit funnily, one possible fix is.

Suggested change
/// this will be a non-Taproot witness v1 output, spendable by anyone under the current
/// consensus rules before one of future softfork would change that.
/// this will be a non-Taproot witness v1 output, spendable by anyone under the current
/// consensus rules (until some future softfork changes that).

Future(WitnessVersion, /** witness program length */ usize),
/// Witness version is 0 and is known to consensus, but witness program has non-standard length
/// which is not supported by consensus rules and can't be spent.
NotSpendableWitnessV0(/** witness program length */ usize),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As others have mentioned, I find this variant very confusing. There are lots of ways to construct provably unspendable outputs, and this one is particularly obscure. It's also not clear that you could actually produce a usable address for something like this, even if you bech32-encoded it, because parsers tend to have length checks.

Similarly NonTaprootWitnessV1 should be merged into Future.

@apoelstra
Copy link
Copy Markdown
Member

Even with the reduced scope I agree with others that this PR has an unclear motivation and purpose. My original comment, that users who want to classify addresses should do so themselves and/or use descriptors (and that if they do, it seems pretty unlikely to me that they would want this particular ad-hoc classification scheme), still stands.

@dr-orlovsky
Copy link
Copy Markdown
Collaborator Author

Ok, closing it

erickcestari pushed a commit to erickcestari/rust-bitcoin that referenced this pull request Feb 4, 2026
7634642 Use struct update syntax (Tobin C. Harding)
2bc083e Use iterator instead of manual loop (Tobin C. Harding)
6d663e1 Allow range loop in test code (Tobin C. Harding)
a8c156e Use map instead of and_then (Tobin C. Harding)
d5662da Remove redundant closure (Tobin C. Harding)
90f1585 Remove redundant clone (Tobin C. Harding)
409d4b0 Remove explicit reference (Tobin C. Harding)
9ec6b17 Remove redundant field names in struct initialization (Tobin C. Harding)

Pull request description:

  After doing rust-bitcoin#668 I wanted to be able to use the `just sane` command before pushing PRs, so fix the lint issues.

ACKs for top commit:
  apoelstra:
    ACK 7634642 thanks! we should re-enabled Clippy in CI in this repo...I believe we disabled it due to a clippy bug in July

Tree-SHA512: 1e953f2fc1a6e649324293a84abf1be54feca6095111653af046aacf17a83e6ae15452fe8825599ccbfae0ea84fe7d1988c8b72d5dd902c9d0fb1d34e660ef8b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API break This PR requires a version bump for the next release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should Address maintain validity invariants?

6 participants