Skip to content

p2p: Implement encoding traits for AddrV2Payload#5762

Merged
apoelstra merged 3 commits into
rust-bitcoin:masterfrom
rustaceanrob:26-3-3-addrv2-pl
Mar 26, 2026
Merged

p2p: Implement encoding traits for AddrV2Payload#5762
apoelstra merged 3 commits into
rust-bitcoin:masterfrom
rustaceanrob:26-3-3-addrv2-pl

Conversation

@rustaceanrob

@rustaceanrob rustaceanrob commented Mar 3, 2026

Copy link
Copy Markdown
Member

Last prerequisite before implementing encoding for NetworkMessage

@github-actions github-actions Bot added the C-p2p label Mar 3, 2026
Comment thread p2p/src/address.rs Outdated
@nyonson

nyonson commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Andrew just wrote up a good summary of the CompactSize restrictions which I think kinda explains your question: #5752 (comment). Basically, we have designed CompactSize codecs around collection lengths. Maybe this service flags use case is showing a gap in the interface? I am not quiet sure the best way forward yet.

@tcharding

Copy link
Copy Markdown
Member

Is this ready for review @rustaceanrob?

@rustaceanrob

Copy link
Copy Markdown
Member Author

I was deciding what to do about the service flag oddities, but I think this should actually be ready to go.

In protocol.h, although the service flags run to uint64_t, they mention 1 << 24 to 1 << 31 as the reserved bits for experimentation. Given we are only at 1 << 11, in both experimentation and normal operation we don't go over 32 bit max. I don't think we will have a case where a signaled flag is silently cut off by the u32 cast until we are old and gray. I can add an in-line comment or mention this in the commit.

@luisschwab what service bits are used for Utreexo?

@rustaceanrob rustaceanrob marked this pull request as ready for review March 9, 2026 07:21
Comment thread p2p/src/address.rs Outdated
Encoder4::new(
ArrayEncoder::without_length_prefix(self.time.to_le_bytes()),
// Silent high-bits cutoff on 32-bit arch
CompactSizeEncoder::new(self.services.to_u64() as usize),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you provide maybe a better description of why 64 bits is needed for the services?

It's usually not preferable to use as to silently truncate bits. Ideally you want to use into(), and if that's not an option, consider an explicit saturate so there's less surprise. Although, I'm having trouble finding a good description of what the upper bits for the services portion of AddrV2 could be. It seems silly that this services field needs 64 bits, so I wonder why there is a need to declare that many different service possibilities..

Lastly, other crates in this workspace do a specific // cast OK comment, for example "cast ok, 64 fits into a u8".

@rustaceanrob rustaceanrob Mar 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Can you provide maybe a better description of why 64 bits is needed for the services?

No, I don't know the history and the documentation states experimental bits only go to 1 << 31 so I am unsure why 64 bits was used.

It seems silly that this services field needs 64 bits

Yes it does.

// cast OK comment, for example

I will adopt that, thanks.

@rustaceanrob rustaceanrob Mar 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated the commit message and in-line code comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks for posting the link to protocol.h, that makes more sense now, sort of. ha, "temporary experiments"..

Maybe it would be better though to just explicitly panic with some such message like "temporary service experiments can not be run on 32bit architectures". It's such a rare case though that I think this is unlikely to ever be encountered. Just that it's much better to avoid using as so people don't need to think too hard about what could happen.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree with @yancyribbens' comment above

@rustaceanrob rustaceanrob force-pushed the 26-3-3-addrv2-pl branch 2 times, most recently from 41127aa to 1ce00fa Compare March 9, 2026 12:24
@luisschwab

Copy link
Copy Markdown
Contributor

@rustaceanrob utreexod and Floresta currently use 1 << 24 (NODE_UTREEXO) and 1 << 25 (NODE_UTREEXO_ARCHIVE), which are in the experimental range. BIP-0183 defines 1 << 12 (NODE_UTREEXO) and 1 << 13 (NODE_UTREEXO_ARCHIVE), which we will migrate to in the near future. I do have a draft PR here to add P2P Utreexo stuff, but I'm waiting for the BIPs to become final to update it and mark it as ready for review.

@yancyribbens

Copy link
Copy Markdown
Contributor

@luisschwab

@rustaceanrob utreexod and Floresta currently use 1 << 24 (NODE_UTREEXO) and 1 << 25 (NODE_UTREEXO_ARCHIVE), which are in the experimental range. BIP-0183 defines 1 << 12 (NODE_UTREEXO) and 1 << 13 (NODE_UTREEXO_ARCHIVE), which we will migrate to in the near future.

Interesting. How is this supposed to work on 32 bit machines, though?

I do have a draft PR here to add P2P Utreexo stuff, but I'm waiting for the BIPs to become final to update it and mark it as ready for review.

Oh cool, Where?

@luisschwab

Copy link
Copy Markdown
Contributor

Interesting. How is this supposed to work on 32 bit machines, though?

Not sure what you mean by this, but 32-bit machines can just use two 32-bit registers to represent an u64. That would only be an issue if we used usize for it, but that's not the case:

/// Flags to indicate which network services a node supports.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceFlags(u64);

Oh cool, Where?

#5009

@yancyribbens

Copy link
Copy Markdown
Contributor

That would only be an issue if we used usize for it, but that's not the case:

Well, we are talking about this call: CompactSizeEncoder::new(self.services.to_u64() as usize) to here which does indeed take a usize.

#5009

Ok yes I've seen that. nice.

@luisschwab

Copy link
Copy Markdown
Contributor

Well, we are talking about this call: CompactSizeEncoder::new(self.services.to_u64() as usize) to here which does indeed take a usize.

Oh, I hadn't looked at the changes here. Utreexo stuff will be fine on 32-bit once we move to the bits defined in BIP-0183.

@rustaceanrob

rustaceanrob commented Mar 10, 2026

Copy link
Copy Markdown
Member Author

Interestingly there are nodes advertising u64::MAX, probably as a sort of fingerprinting or explicit attack: bitcoin/bitcoin#34768

As acknowledged in that thread, these services are unassigned and are meaningless. I updated the first commit to panic if the services are greater than u32::MAX, which is almost certainly malicious and beyond the experimental range. Likewise when decoding, if bits higher than experimental range are assigned, they should be ignored IMO.

@yancyribbens

Copy link
Copy Markdown
Contributor

I updated the first commit to panic if the services are greater than u32::MAX, which is almost certainly malicious and beyond the experimental range.

Thanks. I'm not sure if others have the same opinion, although I would prefer knowing something experimental I'm working on isn't supported by a panic instead of silently succeeding and passing a service as u32::MAX which is what values would be truncated to. I guess this could be debug_assert! which would only panic then in debug mode. Probably not worth worrying about though unless there is an issue that arises?

Sadly we still need the pesky as to do the conversion - When I had suggested adding a panic, I had thought that we could not use as but I guess it's still required.

@yancyribbens

Copy link
Copy Markdown
Contributor

Actually, I'm wondering now if debug_assert would really be the better way to go. The consequence of this change is that in the rare case that someone is running a arch that's 32 bits would have their software blow up if someone on the network was running experimental services. That would be good to know about in debug mode, but in release mode that could be annoying if you just don't care about the service some malicious or experimental node is advertising.

@luisschwab

Copy link
Copy Markdown
Contributor

The consequence of this change is that in the rare case that someone is running a arch that's 32 bits would have their software blow up if someone on the network was running experimental services.

Yes, that's an unnaceptable DoS vector. Floresta and utreexod would make a lot of nodes crash if this was the case.

@yancyribbens

Copy link
Copy Markdown
Contributor

Yes, that's an unnaceptable DoS vector. Floresta and utreexod would make a lot of nodes crash if this was the case.

To be fair, you'd have to be running some pretty ancient setup - 32 bits, no PAE kernel. Still, I think given what you said, debug_assert sounds better IMO

@yancyribbens

Copy link
Copy Markdown
Contributor

It looks like it would be better to wait for #5784 to merge. Since Services is a wrapped u64 no ugly as cast is required then. That means all the comments can be removed. Not sure why nobody else commented here about that. Sorry if I created more work for you.

@rustaceanrob

Copy link
Copy Markdown
Member Author

Putting on ice since #5784 is making good progress

@rustaceanrob rustaceanrob marked this pull request as draft March 12, 2026 14:11
apoelstra added a commit that referenced this pull request Mar 21, 2026
85a6da3 api: update for CompactSizeU64Decoder (Nick Johnson)
bb7bf48 consensus_encoding: const the compact size prefix values (Nick Johnson)
4614515 consensus_encoding: add round trip test for compact size codecs (Nick Johnson)
77a60ae consensus_encoding: add a u64 decoder for compact size (Nick Johnson)
773b4a3 consensus_encoding: add a u64 constructor to compact size encoder (Nick Johnson)
a06bf03 consensus_encoding: move compact size into its own module (Nick Johnson)

Pull request description:

  Add u64 support to the Compact Size codec since there are non prefix length use cases (#5762). Kind of related to #5752.
  
  I believe this is the simplest way to add support, but the asymmetry kind of annoys me. On the encoding side, only a new constructor is necessary. But on the decoding side a new type is required since there is a 1:1 with the associated output. I don't think symmetry is a strong enough reason to put a type on the encoding side though.
  
  I didn't put much though into the names, focusing on minimal change. But maybe it would be worth it to name each encoder/decoder after its use case instead of impl? So the existing one would be called something like `LengthPrefixDecoder` and so on.
  
  The first commit moves the existing compact size stuff into its own `::compact_size` module, to kind of corner this weirdness. This creates kind of a funky dependency graph with `::decode` and `::decode::decoders` (and the equivalent encoders side), but none of that is exposed from the crate since it is a private module.


ACKs for top commit:
  tcharding:
    ACK 85a6da3
  apoelstra:
    ACK 85a6da3; successfully ran local tests


Tree-SHA512: ba3dca8f698ede3538664e29edba3ba2cb5190515233a0b3afef93af656d6ef7538efad7f7173cad38e53263073d5ac6a69be5c940c907cbe5b20181786e23b2
@yancyribbens

Copy link
Copy Markdown
Contributor

Putting on ice since #5784 is making good progress

5784 recently merged, so this work can be resumed.

Member of `AddrV2Payload` which is ultimately apart of
`NetworkMessage`.

Some non-zero number of clients advertise services outside of 32 bits of
precision, which requires decoding arbitrarily large compact sizes. As
these integers do not represent collection lengths and will not be used
for allocation, this is acceptable.

ref: https://github.com/bitcoin/bitcoin/blob/4d7d5f6b79d4c11c47e7a828d81296918fd11d4d/src/protocol.h#L332
ref: bitcoin/bitcoin#34768
Vector counterpart of the previous commit.
@rustaceanrob

Copy link
Copy Markdown
Member Author

Rebased cbfdb80 with the compact size encoding changes

@rustaceanrob rustaceanrob marked this pull request as ready for review March 23, 2026 19:14

@tcharding tcharding left a comment

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.

ACK cbfdb80

@apoelstra apoelstra left a comment

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.

ACK cbfdb80; successfully ran local tests

@apoelstra apoelstra merged commit 0741d87 into rust-bitcoin:master Mar 26, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants