Skip to content

Introduce Payjoin version enum#668

Merged
DanGould merged 1 commit intopayjoin:masterfrom
shinghim:643-version
May 6, 2025
Merged

Introduce Payjoin version enum#668
DanGould merged 1 commit intopayjoin:masterfrom
shinghim:643-version

Conversation

@shinghim
Copy link
Contributor

@shinghim shinghim commented Apr 25, 2025

Fixes #643

@shinghim shinghim force-pushed the 643-version branch 3 times, most recently from 8b05efa to badd2c7 Compare April 25, 2025 15:29
@coveralls
Copy link
Collaborator

coveralls commented Apr 25, 2025

Pull Request Test Coverage Report for Build 14868573150

Details

  • 20 of 28 (71.43%) changed or added relevant lines in 6 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.03%) to 81.995%

Changes Missing Coverage Covered Lines Changed/Added Lines %
payjoin/src/core/version.rs 0 8 0.0%
Totals Coverage Status
Change from base Build 14867604163: -0.03%
Covered Lines: 5442
Relevant Lines: 6637

💛 - Coveralls

@shinghim shinghim marked this pull request as ready for review April 25, 2025 15:39
@DanGould
Copy link
Contributor

have you considered an explicit discriminant enum? I can't imagine our Version enum would ever have fields unless we did something wonky to have sub-versions.

Something like the following is what I was thinking about. I'm not suggesting a change per se but trying to understand the rationale for one over the other if there was one

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Version {
    One = 1,
    Two = 2,
}

@shinghim
Copy link
Contributor Author

@DanGould I hadn't considered it, but I think it makes more sense to have it as an explicit discriminant enum so we don't need the extra logic to convert to a usize. I'm not too familiar with the #[repr(u8)] bit but did some reading, and it looks like it'll be necessary since it might get included in the ffi work

Copy link
Contributor

@DanGould DanGould 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 glad you picked this up and wrote some tests. Thank you.

I wonder which branches aren't tested making our code coverage go down.

I know we use some differing BIP XX/BIP-XX/BIPXX formatting in this repo but at least keep them consistent in one PR please

default = ["v2"]
#[doc = "Core features for payjoin state machines"]
_core = ["bitcoin/rand-std", "serde_json", "url", "bitcoin_uri"]
_core = ["bitcoin/rand-std", "serde_json", "url", "bitcoin_uri", "serde"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change strictly necessary? for Version compilation i.e. all _core-enabled compilations? If so, shouldn't the redundant explicit feature be removed from v2 = ["serde"...] be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's needed for Serialize - I'll remove the redundant feature in v2

Copy link
Contributor

Choose a reason for hiding this comment

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

And is that serde::Serialize required for the v1 feature used exclusively?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From BIP-77:

HPKE binds ciphertexts to application-specific info strings. Because
of this domain separation, BIP 78's v parameter is redundant and
should be omitted for version 2.

If we aren't using the v parameter, we can probably keep it to v1 exclusively

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure our code reflects that suggested omission yet, and it also seems based on your later comment that serde is still required for the error. If for some reason that error that serializes a version array never gets hit, v1 exclusivity would be great. If not, it's ok to keep in core.

DanGould added a commit that referenced this pull request Apr 30, 2025
Closes #647 

Create a new `core` module since there isn't a module to put
common/core stuff. The new Version enum in #668 could also be put into
this mod, among other things
@shinghim shinghim force-pushed the 643-version branch 3 times, most recently from cdce962 to 25f3d44 Compare May 1, 2025 04:27
@shinghim
Copy link
Contributor Author

shinghim commented May 1, 2025

Also moved Version into the new core module

@shinghim shinghim requested a review from DanGould May 1, 2025 04:36
Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

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

ser/de behavior, both having a serializer/deserializer and their feature gated applicability are my only remaining concerns

Comment on lines +32 to +47
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(*self as u8).serialize(serializer)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

is this not missing a matching deserialize impl?

Copy link
Contributor Author

@shinghim shinghim May 6, 2025

Choose a reason for hiding this comment

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

you're right, i'll add it actually, i'm not seeing anywhere it's being used, unlike Serialize, which is being used here:

serde_json::to_string(supported_versions).unwrap_or_default();

is it okay to leave out until we are actually using it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! very well.

In this case, please document the omission and design decisions on the Version type itself. That is, both the docstring on impl fmt::Display (which right now isn't actually linked to Version::fmt because it comes before the impl block), and notes about what is implemented need to go on the Version enum itself and then this is golden.

/// The Payjoin version
///
/// From [BIP 78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#optional-parameters),
/// the supported versions should be output as numbers:
///
/// ```json
/// {
///     "errorCode": "version-unsupported",
///     "supported" : [ 2, 3, 4 ],
///     "message": "The version is not supported anymore"
/// }
/// ```
///
/// # Note
/// - Only [`Serialize`] is implemented for json array serialization in the `unsupported-version` error message,
///   not [`serde::Deserialize`], as deserialization is not required for this type.
/// - [`fmt::Display`] and [`fmt::Debug`] output the `u8` representation for compatibility with BIP 78/77
///   and to match the expected wire format.

Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

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

Thanks for the clarifications. I noticed one more thing. Version should be pub(crate) in lib.rs.

And please put the documentation on the Version type itself so that it's discoverable.

default = ["v2"]
#[doc = "Core features for payjoin state machines"]
_core = ["bitcoin/rand-std", "serde_json", "url", "bitcoin_uri"]
_core = ["bitcoin/rand-std", "serde_json", "url", "bitcoin_uri", "serde"]
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure our code reflects that suggested omission yet, and it also seems based on your later comment that serde is still required for the error. If for some reason that error that serializes a version array never gets hit, v1 exclusivity would be great. If not, it's ok to keep in core.

Comment on lines +32 to +47
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(*self as u8).serialize(serializer)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! very well.

In this case, please document the omission and design decisions on the Version type itself. That is, both the docstring on impl fmt::Display (which right now isn't actually linked to Version::fmt because it comes before the impl block), and notes about what is implemented need to go on the Version enum itself and then this is golden.

/// The Payjoin version
///
/// From [BIP 78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#optional-parameters),
/// the supported versions should be output as numbers:
///
/// ```json
/// {
///     "errorCode": "version-unsupported",
///     "supported" : [ 2, 3, 4 ],
///     "message": "The version is not supported anymore"
/// }
/// ```
///
/// # Note
/// - Only [`Serialize`] is implemented for json array serialization in the `unsupported-version` error message,
///   not [`serde::Deserialize`], as deserialization is not required for this type.
/// - [`fmt::Display`] and [`fmt::Debug`] output the `u8` representation for compatibility with BIP 78/77
///   and to match the expected wire format.

Comment on lines 77 to 78
#[cfg(feature = "_core")]
pub use crate::core::version::Version;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think pub(crate) is sufficient since Version is an internal API

Suggested change
#[cfg(feature = "_core")]
pub use crate::core::version::Version;
#[cfg(feature = "_core")]
pub(crate) use crate::core::version::Version;

In a follow up, all of these internal crate re-exports can be done inside core/mod.rs, and then something like

#[cfg(feature = "_core")]
pub use crate::core::*;

can be used in lib.rs to clean up the verbose syntax here. Thanks for starting the cleanup by adding the core mod 😎

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

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

ACK 292cc79

@DanGould DanGould merged commit a854386 into payjoin:master May 6, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make payjoin version enum, even if only for internal use

5 participants