Skip to content

Commit

Permalink
feat: Add ProtoJSON-compatible Serialize and Deserialize instance…
Browse files Browse the repository at this point in the history
…s on all Protobuf definitions via `pbjson` (cosmos#146)

* Derive `Serialize` and `Deserialize` on all Protobuf definitions via `pbjson`

* Run Clippy on all possible features combinations using `cargo-hack`

* Feature-gate `pbjson` dependency

* Add roundtrip serialization test
  • Loading branch information
romac authored Apr 26, 2023
1 parent d28ea7a commit 58722c1
Show file tree
Hide file tree
Showing 9 changed files with 2,046 additions and 35 deletions.
24 changes: 15 additions & 9 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ concurrency:
cancel-in-progress: true

jobs:
clippy:
name: Clippy
fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -28,17 +28,23 @@ jobs:
working-directory: ./rust
run: cargo fmt -- --check

- name: Clippy (host-functions + std)
working-directory: ./rust
run: cargo clippy --tests -- -D warnings
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- uses: taiki-e/install-action@cargo-hack

- name: Clippy (no-std)
- name: Clippy (features powerset)
working-directory: ./rust
run: cargo clippy --tests --no-default-features -- -D warnings
run: cargo hack clippy --feature-powerset --no-dev-deps -- -D warnings

- name: Clippy (host functions only)
- name: Clippy (features powerset, tests)
working-directory: ./rust
run: cargo clippy --tests --no-default-features --features host-functions -- -D warnings
run: cargo hack clippy --tests --feature-powerset -- -D warnings

test:
name: Test
Expand Down
2 changes: 1 addition & 1 deletion rust/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
target
.idea
Cargo.lock
Cargo.lock
23 changes: 10 additions & 13 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,21 @@ prost = {version = "0.11", default-features = false, features = ["prost-derive"]
ripemd = {version = "0.1.1", optional = true, default-features = false}
sha2 = {version = "0.10.2", optional = true, default-features = false}
sha3 = {version = "0.10.2", optional = true, default-features = false}
serde = {version = "1.0", optional = true, default-features = false}
pbjson = {version = "0.5.1", optional = true}

[dev-dependencies]
ripemd = {version = "0.1.1"}
serde = {version = "1.0.125", features = ["derive"]}
serde = {version = "1.0", features = ["derive"]}
serde_json = {version = "1.0.64"}
sha2 = {version = "0.10.2"}
sha3 = {version = "0.10.2"}

[features]
default = ["std", "host-functions"]
host-functions = [
"sha2",
"sha3",
"ripemd",
]
std = [
"prost/std",
"bytes/std",
"hex/std",
"anyhow/std",
]
default = ["std", "host-functions", "serde"]

std = ["prost/std", "bytes/std", "hex/std", "anyhow/std"]

host-functions = ["sha2", "sha3", "ripemd"]

serde = ["serde/std", "dep:pbjson"]
3 changes: 2 additions & 1 deletion rust/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bytes = "1.0.1"
prost = "0.11"
prost-build = "0.11"
bytes = "1.0.1"
pbjson-build = "0.5.1"
45 changes: 34 additions & 11 deletions rust/codegen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
extern crate prost_build;

use std::env;
use std::io::Result;
use std::path::PathBuf;
use std::vec::Vec;

fn main() {
fn main() -> Result<()> {
let args: Vec<_> = env::args().collect();
let mut root = "../..";
if args.len() > 1 {
root = &args[1];
}
let root = if args.len() > 1 {
&args[1]
} else {
env!("CARGO_MANIFEST_DIR")
};

println!("Root: {root}");

let out_dir: &str = &format!("{}{}", root, "/rust/src");
let input: &str = &format!("{}{}", root, "/proto/cosmos/ics23/v1/proofs.proto");
let root = PathBuf::from(root).join("..").join("..").canonicalize()?;
let input = root.join("proto/cosmos/ics23/v1/proofs.proto");
let out_dir = root.join("rust/src");
let descriptor_path = out_dir.join("proto_descriptor.bin");

println!("Input: {}", input.display());
println!("Output: {}", out_dir.display());
println!("Descriptor: {}", descriptor_path.display());

prost_build::Config::new()
.out_dir(&out_dir)
.format(true)
.compile_protos(&[input], &[root])
.unwrap();
// Needed for pbjson_build to generate the serde implementations
.file_descriptor_set_path(&descriptor_path)
.compile_well_known_types()
// As recommended in pbjson_types docs
.extern_path(".google.protobuf", "::pbjson_types")
.compile_protos(&[input], &[root])?;

// Finally, build pbjson Serialize, Deserialize impls:
let descriptor_set = std::fs::read(descriptor_path)?;

pbjson_build::Builder::new()
.register_descriptors(&descriptor_set)?
.out_dir(&out_dir)
.build(&[".cosmos.ics23.v1"])?;

Ok(())
}
31 changes: 31 additions & 0 deletions rust/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,4 +844,35 @@ mod tests {
x <<= 1;
}
}

#[cfg(feature = "serde")]
#[test]
fn serde_roundtrip() -> Result<()> {
let tests = [
"exist_left",
"exist_right",
"exist_middle",
"nonexist_left",
"nonexist_right",
"nonexist_middle",
];

let specs = ["tendermint", "iavl", "smt"];

let files = tests
.iter()
.flat_map(|test| specs.iter().map(move |spec| (test, spec)))
.map(|(test, spec)| format!("../testdata/{}/{}.json", spec, test));

for file in files {
let (proof, _) = load_file(&file)?;

let json = serde_json::to_string(&proof)?;
let parsed = serde_json::from_str(&json)?;

assert_eq!(proof, parsed);
}

Ok(())
}
}
Loading

0 comments on commit 58722c1

Please sign in to comment.