diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11cb31f..93a14f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,16 +4,76 @@ on: push: branches: - main + - release-* + tags: + - v[0-9]+.[0-9]+.[0-9]+ pull_request: branches: - main - schedule: - - cron: '0 0 * * 1' + - release-* workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: + lint: + runs-on: ubuntu-latest + env: + RUST_LOG: info + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching + + - name: Format Check + run: cargo fmt -- --check + + - name: Clippy + uses: actions-rs-plus/clippy-check@v1 + with: + token: ${{ github.token }} + args: --workspace --all-features --all-targets -- -D warnings + build: runs-on: ubuntu-latest + env: + RUST_LOG: info + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching + + - name: Build + run: cargo build --all-features --all-targets --release + + test: + runs-on: ubuntu-latest + env: + RUST_LOG: info + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching + + - name: Build tests + run: cargo test --workspace --release --all-features --no-run + + - name: Test + run: cargo test --workspace --release --all-features --verbose -- --test-threads 2 + timeout-minutes: 60 + + wasm-test: + runs-on: ubuntu-latest + env: + RUST_LOG: info steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -22,34 +82,62 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Install wasm-pack binary - run: | - wget -O wasm.tar.gz https://github.com/rustwasm/wasm-pack/releases/download/v0.10.2/wasm-pack-v0.10.2-x86_64-unknown-linux-musl.tar.gz - tar -xvf wasm.tar.gz - sudo mv -v wasm*/wasm-pack /usr/bin/ - rm -rv wasm.tar.gz wasm-pack-* + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - name: Clippy - run: | - cargo clippy -- -D warnings + - name: Web Test + run: wasm-pack test --headless --firefox + working-directory: tagged-base64 - - name: Clippy without default features + docs: + runs-on: ubuntu-latest + env: + RUST_LOG: info + steps: + - uses: actions/checkout@v4 + name: Checkout Repository + + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching + + - name: Generate Documentation run: | - cargo clippy --no-default-features -- -D warnings + cargo doc --no-deps --lib --release --all-features + echo '' > target/doc/index.html - - name: Audit - run: cargo audit + - name: Deploy Documentation + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./target/doc + cname: tagged-base64.docs.espressosys.com - - name: Cargo Build - run: cargo build --workspace + semver-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout Repository - - name: Cargo Test - run: cargo test --verbose + - uses: Swatinem/rust-cache@v2 + name: Enable Rust Caching - - name: Web Test - run: wasm-pack test --headless --firefox + - name: Check semver + uses: obi1kenobi/cargo-semver-checks-action@v2 - # Temporarily disable semver check - #- name: Check semver - # uses: obi1kenobi/cargo-semver-checks-action@v2 - # with: - # package: tagged-base64 \ No newline at end of file + publish: + needs: + - build + - test + - wasm-test + - lint + - docs + - semver-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: katyo/publish-crates@v2 + with: + # Only do an actual publish if this is a push to a release tag. Otherwise, do a dry run. + dry-run: ${{ !(github.event_name == 'push' && github.ref_type == 'tag') }} + ignore-unpublished-changes: true + registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4d27c97..0fe27af 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -31,8 +31,8 @@ jobs: args: --all-features --no-fail-fast env: CARGO_INCREMENTAL: '0' - RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' - RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' + RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off' + RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off' - name: Install grcov run: | diff --git a/Cargo.toml b/Cargo.toml index dcf7bb1..41cdd55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,64 +1,26 @@ -[package] -name = "tagged-base64" -version = "0.3.4" +[workspace.package] +version = "0.4.0" authors = ["Espresso Systems "] edition = "2021" -description = "User-oriented format for binary data. Tagged Base64 is intended to be used in user interfaces including URLs and text to be copied and pasted without the need for additional encoding, such as quoting or escape sequences." -repository = "https://github.com/EspressoSystems/tagged-base64" license = "MIT" -[lib] -crate-type = ["cdylib", "rlib"] - -[[bin]] -name = "tagged-base64" -required-features = ["build-cli"] - -[features] -default = ["ark-serialize", "serde", "wasm-bindgen"] -ark-serialize = ["dep:ark-serialize"] -serde = ["dep:serde", "tagged-base64-macros/serde"] -wasm-bindgen = ["dep:wasm-bindgen"] -wasm-debug = ["dep:console_error_panic_hook"] -build-cli = ["dep:clap"] - -[dependencies] -ark-serialize = { version = "0.4.0", optional = true, default-features = false, features = ["derive"] } -ark-std = { version = "0.4.0", default-features = false } +[workspace] +resolver = "2" +members = [ + "tagged-base64", + "tagged-base64-macros", +] + +[workspace.dependencies] +ark-bls12-381 = "0.4" +ark-serialize = { version = "0.4", default-features = false } +ark-std = { version = "0.4", default-features = false } base64 = "0.22" -crc-any = { version = "2.4.1", default-features = false } -serde = { version = "1.0", optional = true, features = ["derive"] } -snafu = { version = "0.8" } -tagged-base64-macros = { version = "0.3.3", path = "macros", default-features = false } - -# Command line argument processing -clap = { version = "4.0", optional = true, features = ["derive"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2.78", features = ["serde-serialize"], optional = true } - -# required for debugging from wasm -web-sys = { version = "0.3.49", optional = true, features = ["console", "Headers", "RequestInit", "RequestMode", "Request", "Response", "Window"] } - -# The `console_error_panic_hook` crate provides better debugging of panics by -# logging them with `console.error`. This is great for development, but requires -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for -# code size when deploying. -console_error_panic_hook = { version = "0.1.7", optional = true } - -[dev-dependencies] -bincode = "1.3" -getrandom = { version = "0.2", features = ["js"] } -quickcheck = "1.0" -quickcheck_macros = "1.0" -serde_json = "1.0" -wasm-bindgen-test = { version = "0.3.28" } +rand_chacha = "0.3" +serde = "1.0" +snafu = "0.8" [profile.release] # Tell `rustc` to optimize for small code size. opt-level = "s" debug = true - -# https://github.com/rustwasm/wasm-bindgen/issues/2279 -[package.metadata.wasm-pack.profile.release] -wasm-opt = ["-Os", "--enable-mutable-globals"] diff --git a/macros/Cargo.toml b/tagged-base64-macros/Cargo.toml similarity index 61% rename from macros/Cargo.toml rename to tagged-base64-macros/Cargo.toml index 17fada1..82aa887 100644 --- a/macros/Cargo.toml +++ b/tagged-base64-macros/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "tagged-base64-macros" description = "Procedural macros associated with tagged-base64" -version = "0.3.3" -authors = ["Espresso Systems "] -edition = "2021" -repository = "https://github.com/EspressoSystems/tagged-base64" -license = "MIT" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } [lib] proc-macro = true diff --git a/macros/src/lib.rs b/tagged-base64-macros/src/lib.rs similarity index 58% rename from macros/src/lib.rs rename to tagged-base64-macros/src/lib.rs index 16c1f00..9b3ca1e 100644 --- a/macros/src/lib.rs +++ b/tagged-base64-macros/src/lib.rs @@ -7,75 +7,6 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, AttributeArgs, Item, Meta, NestedMeta}; -/// Derive serdes for a type which serializes as a binary blob. -/// -/// This macro can be used to easily derive friendly serde implementations for a binary type which -/// implements [CanonicalSerialize](ark_serialize::CanonicalSerialize) and -/// [CanonicalDeserialize](ark_serialize::CanonicalDeserialize). This is useful for cryptographic -/// primitives and other types which do not have a human-readable serialization, but which may be -/// embedded in structs with a human-readable serialization. The serde implementations derived by -/// this macro will serialize the type as bytes for binary encodings and as base 64 for human -/// readable encodings. -/// -/// This macro takes at least one arguments: -/// * The first argument should be the tag, as a string literal or expression. -/// * By default, the derived implementation invokes `CanonicalSerialize` and `CanonicalDeserialize` -/// with `uncompressed` and `unchecked` flags. -/// * If `compressed` and/or `checked` flags are presented, the derived implementation will behave -/// accordingly. -/// -/// Specifically, this macro does 4 things when applied to a type definition: -/// * It adds `#[derive(Serialize, Deserialize)]` to the type definition, along with serde -/// attributes to serialize using [TaggedBase64]. -/// * It creates an implementation of [Tagged] for the type using the specified tag. This tag will -/// be used to identify base 64 strings which represent this type in human-readable encodings. -/// * It creates an implementation of `TryFrom` for the type `T`, which is needed to -/// make the `serde(try_from)` attribute work. -/// * It creates implementations of [Display](ark_std::fmt::Display) and -/// [FromStr](ark_std::str::FromStr) using tagged base 64 as a display format. This allows tagged -/// blob types to be conveniently displayed and read to and from user interfaces in a manner -/// consistent with how they are serialized. -/// -/// Usage example: -/// -/// ``` -/// #[macro_use] extern crate tagged_base64_macros; -/// use ark_serialize::*; -/// -/// #[tagged("PRIM")] -/// #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] -/// pub struct CryptoPrim( -/// // This type can only be serialied as an opaque, binary blob using ark_serialize. -/// pub(crate) ark_bls12_381::Fr, -/// ); -/// ``` -/// -/// The type `CryptoPrim` can now be serialized as binary: -/// ``` -/// # use ark_serialize::*; -/// # use ark_std::UniformRand; -/// # use tagged_base64_macros::tagged; -/// # use rand_chacha::{ChaChaRng, rand_core::SeedableRng}; -/// # #[tagged("PRIM", compressed)] -/// # #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] -/// # struct CryptoPrim(ark_bls12_381::Fr); -/// # let crypto_prim = CryptoPrim(ark_bls12_381::Fr::rand(&mut ChaChaRng::from_seed([42; 32]))); -/// bincode::serialize(&crypto_prim).unwrap(); -/// ``` -/// or as base64: -/// ``` -/// # use ark_serialize::*; -/// # use ark_std::UniformRand; -/// # use tagged_base64_macros::tagged; -/// # use rand_chacha::{ChaChaRng, rand_core::SeedableRng}; -/// # #[tagged("PRIM", compressed, checked)] -/// # #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] -/// # struct CryptoPrim(ark_bls12_381::Fr); -/// # let crypto_prim = CryptoPrim(ark_bls12_381::Fr::rand(&mut ChaChaRng::from_seed([42; 32]))); -/// serde_json::to_string(&crypto_prim).unwrap(); -/// ``` -/// which will produce a tagged base64 string like -/// "PRIM~8oaujwbov8h4eEq7HFpqW6mIXhVbtJGxLUgiKrGpMCoJ". #[proc_macro_attribute] pub fn tagged(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as AttributeArgs); @@ -96,7 +27,7 @@ pub fn tagged(args: TokenStream, input: TokenStream) -> TokenStream { x ), }; - marks.into_iter().for_each(|attr| match attr { + marks.iter().for_each(|attr| match attr { NestedMeta::Meta(Meta::Path(path)) => { if path.is_ident("compressed") { compressed = true; @@ -119,12 +50,10 @@ pub fn tagged(args: TokenStream, input: TokenStream) -> TokenStream { } else { quote!(deserialize_compressed_unchecked) } + } else if checked { + quote!(deserialize_uncompressed) } else { - if checked { - quote!(deserialize_uncompressed) - } else { - quote!(deserialize_uncompressed_unchecked) - } + quote!(deserialize_uncompressed_unchecked) }; #[cfg(feature = "serde")] diff --git a/tagged-base64/Cargo.toml b/tagged-base64/Cargo.toml new file mode 100644 index 0000000..103c3c2 --- /dev/null +++ b/tagged-base64/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "tagged-base64" +description = "User-oriented format for binary data. Tagged Base64 is intended to be used in user interfaces including URLs and text to be copied and pasted without the need for additional encoding, such as quoting or escape sequences." +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[[bin]] +name = "tagged-base64" +required-features = ["build-cli"] + +[features] +default = ["ark-serialize", "serde", "wasm-bindgen"] +ark-serialize = ["dep:ark-serialize"] +serde = ["dep:serde", "tagged-base64-macros/serde"] +wasm-bindgen = ["dep:wasm-bindgen"] +wasm-debug = ["dep:console_error_panic_hook"] +build-cli = ["dep:clap"] + +[dependencies] +ark-serialize = { workspace = true, optional = true, features = ["derive"] } +ark-std = { workspace = true } +base64 = { workspace = true } +crc-any = { version = "2.4.1", default-features = false } +serde = { workspace = true, optional = true, features = ["derive"] } +snafu = { workspace = true } +tagged-base64-macros = { version = "0.4.0", path = "../tagged-base64-macros", default-features = false } + +# Command line argument processing +clap = { version = "4.0", optional = true, features = ["derive"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2.78", features = ["serde-serialize"], optional = true } + +# required for debugging from wasm +web-sys = { version = "0.3.49", optional = true, features = ["console", "Headers", "RequestInit", "RequestMode", "Request", "Response", "Window"] } + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.7", optional = true } + +[dev-dependencies] +ark-bls12-381 = { workspace = true } +bincode = "1.3" +getrandom = { version = "0.2", features = ["js"] } +quickcheck = "1.0" +quickcheck_macros = "1.0" +rand_chacha = "0.3" +serde_json = "1.0" +wasm-bindgen-test = { version = "0.3.28" } + +# https://github.com/rustwasm/wasm-bindgen/issues/2279 +[package.metadata.wasm-pack.profile.release] +wasm-opt = ["-Os", "--enable-mutable-globals"] diff --git a/src/lib.rs b/tagged-base64/src/lib.rs similarity index 81% rename from src/lib.rs rename to tagged-base64/src/lib.rs index a5bc3f4..3e12b37 100644 --- a/src/lib.rs +++ b/tagged-base64/src/lib.rs @@ -69,6 +69,75 @@ use ark_std::{ #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] use wasm_bindgen::prelude::*; +/// Derive serdes for a type which serializes as a binary blob. +/// +/// This macro can be used to easily derive friendly serde implementations for a binary type which +/// implements [CanonicalSerialize](ark_serialize::CanonicalSerialize) and +/// [CanonicalDeserialize](ark_serialize::CanonicalDeserialize). This is useful for cryptographic +/// primitives and other types which do not have a human-readable serialization, but which may be +/// embedded in structs with a human-readable serialization. The serde implementations derived by +/// this macro will serialize the type as bytes for binary encodings and as base 64 for human +/// readable encodings. +/// +/// This macro takes at least one arguments: +/// * The first argument should be the tag, as a string literal or expression. +/// * By default, the derived implementation invokes `CanonicalSerialize` and `CanonicalDeserialize` +/// with `uncompressed` and `unchecked` flags. +/// * If `compressed` and/or `checked` flags are presented, the derived implementation will behave +/// accordingly. +/// +/// Specifically, this macro does 4 things when applied to a type definition: +/// * It adds `#[derive(Serialize, Deserialize)]` to the type definition, along with serde +/// attributes to serialize using [TaggedBase64]. +/// * It creates an implementation of [Tagged] for the type using the specified tag. This tag will +/// be used to identify base 64 strings which represent this type in human-readable encodings. +/// * It creates an implementation of `TryFrom` for the type `T`, which is needed to +/// make the `serde(try_from)` attribute work. +/// * It creates implementations of [Display](ark_std::fmt::Display) and +/// [FromStr](ark_std::str::FromStr) using tagged base 64 as a display format. This allows tagged +/// blob types to be conveniently displayed and read to and from user interfaces in a manner +/// consistent with how they are serialized. +/// +/// Usage example: +/// +/// ``` +/// use ark_serialize::*; +/// use tagged_base64_macros::tagged; +/// +/// #[tagged("PRIM")] +/// #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] +/// pub struct CryptoPrim( +/// // This type can only be serialied as an opaque, binary blob using ark_serialize. +/// pub(crate) ark_bls12_381::Fr, +/// ); +/// ``` +/// +/// The type `CryptoPrim` can now be serialized as binary: +/// ``` +/// # use ark_serialize::*; +/// # use ark_std::UniformRand; +/// # use tagged_base64_macros::tagged; +/// # use rand_chacha::{ChaChaRng, rand_core::SeedableRng}; +/// # #[tagged("PRIM", compressed)] +/// # #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] +/// # struct CryptoPrim(ark_bls12_381::Fr); +/// # let crypto_prim = CryptoPrim(ark_bls12_381::Fr::rand(&mut ChaChaRng::from_seed([42; 32]))); +/// bincode::serialize(&crypto_prim).unwrap(); +/// ``` +/// or as base64: +/// ``` +/// # use ark_serialize::*; +/// # use ark_std::UniformRand; +/// # use tagged_base64_macros::tagged; +/// # use rand_chacha::{ChaChaRng, rand_core::SeedableRng}; +/// # #[tagged("PRIM", compressed, checked)] +/// # #[derive(Clone, CanonicalSerialize, CanonicalDeserialize, /* any other derives */)] +/// # struct CryptoPrim(ark_bls12_381::Fr); +/// # let crypto_prim = CryptoPrim(ark_bls12_381::Fr::rand(&mut ChaChaRng::from_seed([42; 32]))); +/// serde_json::to_string(&crypto_prim).unwrap(); +/// ``` +/// which will produce a tagged base64 string like +/// "PRIM~8oaujwbov8h4eEq7HFpqW6mIXhVbtJGxLUgiKrGpMCoJ". pub use tagged_base64_macros::tagged; /// Separator that does not appear in URL-safe base64 encoding and can diff --git a/src/main.rs b/tagged-base64/src/main.rs similarity index 100% rename from src/main.rs rename to tagged-base64/src/main.rs diff --git a/tests/tests.rs b/tagged-base64/tests/tests.rs similarity index 100% rename from tests/tests.rs rename to tagged-base64/tests/tests.rs