Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,18 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: "Build fuzz targets"
run: cd fuzz && cargo build

Embedded:
name: Embedded build
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
- name: "Use cache"
uses: Swatinem/rust-cache@v2
- name: "Install nix"
uses: DeterminateSystems/determinate-nix-action@main
- name: "Use nix cache"
uses: DeterminateSystems/magic-nix-cache-action@main
- name: "Build embedded target"
run: nix develop .#embedded -c cargo build -p payjoin --no-default-features --features "alloc,v2" --target thumbv7em-none-eabihf -Zbuild-std=core,alloc
19 changes: 19 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,24 @@
AR_wasm32_unknown_unknown = "${pkgs.llvmPackages.bintools-unwrapped}/bin/llvm-ar";
};

embeddedRustToolchain = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override {
extensions = [
"rust-src"
"rustfmt"
"llvm-tools-preview"
];
targets = [ "thumbv7em-none-eabihf" ];
};

embeddedDevShell = pkgs.mkShell {
name = "embedded-dev";
packages = with pkgs; [
embeddedRustToolchain
gcc-arm-embedded
];
CC_thumbv7em_none_eabihf = "arm-none-eabi-gcc";
};

dartDevShell = pkgs.mkShell {
name = "dart-dev";
packages =
Expand Down Expand Up @@ -461,6 +479,7 @@
javascript = javascriptDevShell;
csharp = csharpDevShell;
dart = dartDevShell;
embedded = embeddedDevShell;
};
formatter = treefmtEval.config.build.wrapper;
checks =
Expand Down
43 changes: 28 additions & 15 deletions payjoin/Cargo.toml
Comment thread
benalleng marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,36 @@ exclude = ["tests"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["v2"]
#[doc = "Core features for payjoin state machines"]
_core = [
default = ["std", "v2", "v2-ohttp"]

alloc = []

std = [
"alloc",
"bitcoin/rand",
"bitcoin/rand-std",
"dep:http",
"bitcoin/base64",
"serde_json",
"dep:percent-encoding-rfc3986",
"bitcoin_uri",
"serde",
"bitcoin/serde",
]
directory = []
v1 = ["_core"]
v2 = ["_core", "hpke", "hkdf", "bhttp", "ohttp", "directory"]
#[doc = "Core features for payjoin state machines"]
_core = ["alloc", "serde", "bitcoin/serde"]

directory = ["alloc", "_core"]
v1 = ["_core", "std"]

v2 = ["_core", "directory", "hkdf"]

v2-ohttp = ["v2", "std", "dep:hpke", "dep:bhttp", "dep:ohttp", "dep:http"]
#[doc = "Functions to fetch OHTTP keys via CONNECT proxy using reqwest. Enables `v2` since only `v2` uses OHTTP."]
io = ["v2", "reqwest/rustls-tls"]
_manual-tls = ["rustls"]
io = ["v2-ohttp", "reqwest/rustls-tls", "dep:reqwest"]
_manual-tls = ["reqwest/rustls-tls", "rustls"]

[dependencies]
bhttp = { version = "0.6.1", optional = true }
bitcoin = { version = "0.32.9", features = ["base64"] }
bitcoin-units = "0.1.3"
bitcoin = { version = "0.32.9", default-features = false }
bitcoin-units = { version = "0.1.3", default-features = false }
bitcoin_uri = { version = "0.1.0", optional = true }
hkdf = { version = "0.12.3", optional = true }
hpke = { package = "bitcoin-hpke", version = "0.13.0", optional = true }
Expand All @@ -53,7 +61,9 @@ rustls = { version = "0.23.38", optional = true, default-features = false, featu
] }
serde = { version = "1.0.228", default-features = false, optional = true }
serde_json = { version = "1.0.149", optional = true }
tracing = "0.1.41"
tracing = { version = "0.1.41", default-features = false, features = [
"attributes",
] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-time = "1.1.0"
Expand All @@ -66,7 +76,10 @@ ignored = ["bitcoin-units", "hkdf"]
once_cell = "1.21.3"
payjoin-test-utils = { path = "../payjoin-test-utils", features = ["v2"] }
tokio = { version = "1.52.3", features = ["full"] }
tracing = "0.1.41"
tracing = { version = "0.1.41", default-features = false, features = [
"attributes",
] }


[package.metadata.docs.rs]
all-features = true
Expand Down
5 changes: 5 additions & 0 deletions payjoin/src/bech32.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use bitcoin::bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};
use bitcoin::bech32::{self, EncodeError, Hrp, NoChecksum};

Expand Down
38 changes: 32 additions & 6 deletions payjoin/src/core/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
use std::fmt::Debug;
use std::{error, fmt};
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use core::error;
use core::fmt::{self, Debug};
#[cfg(feature = "std")]
use std::error;

#[derive(Debug)]
pub struct StdRequiredError;

impl fmt::Display for StdRequiredError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "std is required for this operation")
}
}

#[cfg(feature = "std")]
impl std::error::Error for StdRequiredError {}

#[cfg(not(feature = "std"))]
impl core::error::Error for StdRequiredError {}

impl ImplementationError {
pub fn std_required() -> Self { ImplementationError(Box::new(StdRequiredError)) }
}
#[derive(Debug)]
pub struct ImplementationError(Box<dyn error::Error + Send + Sync>);

Expand All @@ -11,7 +34,7 @@ impl ImplementationError {
}

impl fmt::Display for ImplementationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { std::fmt::Display::fmt(&self.0, f) }
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}

impl error::Error for ImplementationError {
Expand All @@ -34,16 +57,17 @@ impl From<&str> for ImplementationError {
ImplementationError::from(error)
}
}

/// Errors that can occur when replaying a session event log
#[cfg(feature = "v2")]
#[derive(Debug)]
pub struct ReplayError<SessionState, SessionEvent>(InternalReplayError<SessionState, SessionEvent>);

#[cfg(feature = "v2")]
impl<SessionState: Debug, SessionEvent: Debug> std::fmt::Display
impl<SessionState: Debug, SessionEvent: Debug> fmt::Display
for ReplayError<SessionState, SessionEvent>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InternalReplayError::*;
match &self.0 {
NoEvents => write!(f, "No events found in session"),
Expand All @@ -56,8 +80,9 @@ impl<SessionState: Debug, SessionEvent: Debug> std::fmt::Display
}
}
}

#[cfg(feature = "v2")]
impl<SessionState: Debug, SessionEvent: Debug> std::error::Error
impl<SessionState: Debug, SessionEvent: Debug> error::Error
for ReplayError<SessionState, SessionEvent>
{
}
Expand All @@ -78,6 +103,7 @@ impl<SessionState, SessionEvent> ReplayError<SessionState, SessionEvent> {

#[cfg(feature = "v2")]
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) enum InternalReplayError<SessionState, SessionEvent> {
/// No events in the event log
NoEvents,
Expand Down
9 changes: 8 additions & 1 deletion payjoin/src/core/hpke.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#![cfg(any(feature = "v2", feature = "v2-ohttp"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use core::error;
use core::fmt;
use core::ops::Deref;
#[cfg(feature = "std")]
use std::error;
use std::ops::Deref;

use bitcoin::key::constants::{ELLSWIFT_ENCODING_SIZE, PUBLIC_KEY_SIZE};
use bitcoin::secp256k1;
Expand Down Expand Up @@ -195,6 +200,7 @@ pub fn decrypt_message_a(
message_a: &[u8],
receiver_sk: &HpkeSecretKey,
) -> Result<(Vec<u8>, HpkePublicKey), HpkeError> {
#[cfg(feature = "std")]
use std::io::{Cursor, Read};

let mut cursor = Cursor::new(message_a);
Expand Down Expand Up @@ -243,6 +249,7 @@ pub fn encrypt_message_b(
Ok(message_b)
}

#[cfg(feature = "std")]
pub fn decrypt_message_b(
message_b: &[u8],
receiver_pk: HpkePublicKey,
Expand Down
9 changes: 6 additions & 3 deletions payjoin/src/core/into_url.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use alloc::string::String;
use core::{error, fmt};

use crate::core::{Url, UrlParseError};

#[derive(Debug, PartialEq, Eq)]
Expand All @@ -6,8 +9,8 @@ pub enum Error {
ParseError(UrlParseError),
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Error::*;

match self {
Expand All @@ -17,7 +20,7 @@ impl std::fmt::Display for Error {
}
}

impl std::error::Error for Error {}
impl error::Error for Error {}

impl From<UrlParseError> for Error {
fn from(err: UrlParseError) -> Error { Error::ParseError(err) }
Expand Down
1 change: 1 addition & 0 deletions payjoin/src/core/io.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! IO-related types and functions. Specifically, fetching OHTTP keys from a payjoin directory.
#[cfg(feature = "std")]
use std::time::Duration;

use http::header::ACCEPT;
Expand Down
22 changes: 17 additions & 5 deletions payjoin/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,42 @@ pub mod receive;
mod request;
pub mod send;
pub use request::*;
#[cfg(any(feature = "v1", feature = "v2-ohttp"))]
pub(crate) mod into_url;
#[cfg(any(feature = "v1", feature = "v2-ohttp"))]
pub use into_url::{Error as IntoUrlError, IntoUrl};
pub(crate) mod url;
#[cfg(feature = "v2-ohttp")]
pub use url::{ParseError as UrlParseError, Url};

#[cfg(not(feature = "v2-ohttp"))]
pub use crate::core::url::{ParseError as UrlParseError, Url};
#[cfg(feature = "v2")]
pub mod time;
pub mod uri;
pub use uri::{PjParam, PjParseError, PjUri, Uri, UriExt};
#[cfg(feature = "std")]
pub use uri::PjUri;
pub use uri::{PjParam, PjParseError};
#[cfg(feature = "std")]
pub use uri::{Uri, UriExt};
pub(crate) mod error_codes;

pub(crate) mod output_substitution;
#[cfg(feature = "v1")]
pub use output_substitution::OutputSubstitution;

#[cfg(feature = "v2")]
#[cfg(feature = "v2-ohttp")]
pub(crate) mod hpke;
#[cfg(feature = "v2")]
pub mod persist;
#[cfg(feature = "v2")]
#[cfg(feature = "v2-ohttp")]
pub use crate::hpke::{HpkeKeyPair, HpkePublicKey};
#[cfg(feature = "v2")]
#[cfg(feature = "v2-ohttp")]
pub(crate) mod ohttp;
#[cfg(feature = "v2")]
#[cfg(feature = "v2-ohttp")]
pub use crate::ohttp::OhttpKeys;
#[cfg(test)]
mod no_std_tests;

#[cfg(feature = "io")]
#[cfg_attr(docsrs, doc(cfg(feature = "io")))]
Expand Down
Loading
Loading