From bca2a6bcb576f3afa94ecda87b4c9c79475cef46 Mon Sep 17 00:00:00 2001
From: martyall <martyall@protonmail.com>
Date: Tue, 14 Jan 2025 16:01:17 -0800
Subject: [PATCH 1/3] first saffron commit: round-trip bytes conversion

---
 Cargo.lock                                    | 19 +++++++++
 Cargo.toml                                    |  4 +-
 saffron/Cargo.toml                            | 28 +++++++++++++
 .../proptest-regressions/serialization.txt    |  7 ++++
 saffron/src/lib.rs                            |  1 +
 saffron/src/main.rs                           |  7 ++++
 saffron/src/serialization.rs                  | 39 +++++++++++++++++++
 7 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 saffron/Cargo.toml
 create mode 100644 saffron/proptest-regressions/serialization.txt
 create mode 100644 saffron/src/lib.rs
 create mode 100644 saffron/src/main.rs
 create mode 100644 saffron/src/serialization.rs

diff --git a/Cargo.lock b/Cargo.lock
index 456d4ce2d5..78d44726dd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -122,6 +122,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+
 [[package]]
 name = "ark-algebra-test-templates"
 version = "0.4.2"
@@ -1909,6 +1915,7 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
 dependencies = [
+ "arbitrary",
  "autocfg",
  "num-integer",
  "num-traits",
@@ -2594,6 +2601,18 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
+[[package]]
+name = "saffron"
+version = "0.1.0"
+dependencies = [
+ "ark-ff",
+ "ark-std",
+ "clap 4.4.18",
+ "mina-curves",
+ "num-bigint",
+ "proptest",
+]
+
 [[package]]
 name = "same-file"
 version = "1.0.6"
diff --git a/Cargo.toml b/Cargo.toml
index 6e35076b45..54e2a8e6c7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,7 +18,8 @@ members = [
     "utils",
     "internal-tracing",
     "ivc",
-    "folding"
+    "folding",
+    "saffron"
 ]
 resolver = "2"
 
@@ -96,6 +97,7 @@ o1-utils = { path = "./utils", version = "0.1.0" }
 o1vm = { path = "./o1vm", version = "0.1.0" }
 optimism = { path = "./optimism", version = "0.1.0" }
 poly-commitment = { path = "./poly-commitment", version = "0.1.0" }
+saffron = { path = "./poly-commitment", version = "0.1.0" }
 signer = { path = "./signer", version = "0.1.0" }
 turshi = { path = "./turshi", version = "0.1.0" }
 utils = { path = "./utils", version = "0.1.0" }
diff --git a/saffron/Cargo.toml b/saffron/Cargo.toml
new file mode 100644
index 0000000000..c3b10fb3d1
--- /dev/null
+++ b/saffron/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "saffron"
+version = "0.1.0"
+description = "He who controls the spice controls the universe."
+repository = "https://github.com/o1-labs/proof-systems"
+homepage = "https://o1-labs.github.io/proof-systems/"
+documentation = "https://o1-labs.github.io/proof-systems/rustdoc/"
+readme = "README.md"
+edition = "2021"
+license = "Apache-2.0"
+
+# [lib]
+# path = "src/lib.rs"
+
+[[bin]]
+name = "saffron"
+path = "src/main.rs"
+
+[dependencies]
+ark-ff.workspace = true
+clap = { workspace = true, features = ["derive"] }
+mina-curves.workspace = true
+num-bigint.workspace = true
+
+[dev-dependencies]
+proptest.workspace = true
+num-bigint = {workspace = true, features = ["arbitrary"]}
+ark-std.workspace = true
\ No newline at end of file
diff --git a/saffron/proptest-regressions/serialization.txt b/saffron/proptest-regressions/serialization.txt
new file mode 100644
index 0000000000..bb63fed4f0
--- /dev/null
+++ b/saffron/proptest-regressions/serialization.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc cc009231a66ff36398e931c9374d5f97750d5af78e8df38a905f079981879b3d # shrinks to bs = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
diff --git a/saffron/src/lib.rs b/saffron/src/lib.rs
new file mode 100644
index 0000000000..16561bfd8d
--- /dev/null
+++ b/saffron/src/lib.rs
@@ -0,0 +1 @@
+pub mod serialization;
diff --git a/saffron/src/main.rs b/saffron/src/main.rs
new file mode 100644
index 0000000000..425059571a
--- /dev/null
+++ b/saffron/src/main.rs
@@ -0,0 +1,7 @@
+use saffron::serialization::from_bytes;
+
+fn main() {
+    let bs = vec![5u8];
+    let n = from_bytes(&bs);
+    println!("Hello, world {}!", n);
+}
diff --git a/saffron/src/serialization.rs b/saffron/src/serialization.rs
new file mode 100644
index 0000000000..3d2e7e0b61
--- /dev/null
+++ b/saffron/src/serialization.rs
@@ -0,0 +1,39 @@
+use mina_curves::pasta::Fp;
+use num_bigint::BigUint;
+
+pub fn from_bytes(bs: &[u8]) -> Fp {
+    BigUint::from_bytes_be(bs).into()
+}
+
+pub fn to_bytes(f: Fp) -> Vec<u8> {
+    BigUint::from(f).to_bytes_be()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn test_round_trip_from_bytes(bs in any::<[u8;31]>())
+          { let n = from_bytes(&bs);
+            let bs2 = to_bytes(n);
+            prop_assert_eq!(bs, bs2.as_slice());
+          }
+    }
+
+    use ark_std::UniformRand;
+
+    proptest! {
+        #[test]
+        fn test_round_trip_from_fp(
+            // Generate a valid field element using the curve's RNG
+            a in prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
+        ) {
+            let bs = to_bytes(a);
+            let a2 = from_bytes(&bs);
+            prop_assert_eq!(a, a2);
+        }
+    }
+}

From 47f55092121e4d1dc1151e6ed6b2d2a4a7bebf61 Mon Sep 17 00:00:00 2001
From: martyall <martyall@protonmail.com>
Date: Wed, 15 Jan 2025 09:28:18 -0800
Subject: [PATCH 2/3] get e2e test working

---
 Cargo.lock                                    | 298 ++++++++++++++++--
 saffron/.gitignore                            |   1 +
 saffron/Cargo.toml                            |  15 +-
 saffron/fixtures/.gitignore                   |   1 +
 saffron/fixtures/lorem-bin                    | Bin 0 -> 488 bytes
 saffron/fixtures/lorem.txt                    |   1 +
 saffron/lorem.txt                             | Bin 0 -> 465 bytes
 .../proptest-regressions/serialization.txt    |   7 -
 saffron/src/cli.rs                            |  50 +++
 saffron/src/lib.rs                            |   2 +
 saffron/src/main.rs                           |  53 +++-
 saffron/src/serialization.rs                  |  62 +++-
 12 files changed, 433 insertions(+), 57 deletions(-)
 create mode 100644 saffron/.gitignore
 create mode 100644 saffron/fixtures/.gitignore
 create mode 100644 saffron/fixtures/lorem-bin
 create mode 100644 saffron/fixtures/lorem.txt
 create mode 100644 saffron/lorem.txt
 delete mode 100644 saffron/proptest-regressions/serialization.txt
 create mode 100644 saffron/src/cli.rs

diff --git a/Cargo.lock b/Cargo.lock
index 78d44726dd..4844e18284 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -123,10 +123,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "arbitrary"
-version = "1.4.1"
+name = "anyhow"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
 
 [[package]]
 name = "ark-algebra-test-templates"
@@ -491,6 +491,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
+[[package]]
+name = "bytes"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+
 [[package]]
 name = "cargo-spec"
 version = "0.5.0"
@@ -651,7 +657,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -938,7 +944,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim 0.10.0",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -949,7 +955,7 @@ checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -1088,7 +1094,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.4.1",
  "windows-sys 0.52.0",
 ]
 
@@ -1181,6 +1187,95 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
 
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.14.7"
@@ -1652,6 +1747,16 @@ version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.20"
@@ -1821,6 +1926,17 @@ dependencies = [
  "winapi 0.2.8",
 ]
 
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "mio-extras"
 version = "2.0.6"
@@ -1829,7 +1945,7 @@ checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
 dependencies = [
  "lazycell",
  "log",
- "mio",
+ "mio 0.6.23",
  "slab",
 ]
 
@@ -1903,7 +2019,7 @@ dependencies = [
  "fsevent-sys",
  "inotify",
  "libc",
- "mio",
+ "mio 0.6.23",
  "mio-extras",
  "walkdir",
  "winapi 0.3.9",
@@ -1915,7 +2031,6 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
 dependencies = [
- "arbitrary",
  "autocfg",
  "num-integer",
  "num-traits",
@@ -1931,7 +2046,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -1954,6 +2069,16 @@ dependencies = [
  "libm",
 ]
 
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.4",
+ "libc",
+]
+
 [[package]]
 name = "o1-utils"
 version = "0.1.0"
@@ -2162,6 +2287,29 @@ version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
 
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "redox_syscall 0.5.8",
+ "smallvec",
+ "windows-targets 0.52.0",
+]
+
 [[package]]
 name = "paste"
 version = "1.0.14"
@@ -2205,7 +2353,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -2219,6 +2367,18 @@ dependencies = [
  "sha2",
 ]
 
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.29"
@@ -2480,6 +2640,15 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
+dependencies = [
+ "bitflags 2.4.2",
+]
+
 [[package]]
 name = "regex"
 version = "1.10.3"
@@ -2605,12 +2774,19 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 name = "saffron"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "ark-ff",
+ "ark-serialize",
  "ark-std",
+ "bytes",
  "clap 4.4.18",
+ "futures",
  "mina-curves",
- "num-bigint",
+ "o1-utils",
  "proptest",
+ "serde",
+ "tokio",
+ "tokio-util",
 ]
 
 [[package]]
@@ -2622,6 +2798,12 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
 [[package]]
 name = "secp256k1"
 version = "0.28.2"
@@ -2673,7 +2855,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -2713,7 +2895,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -2743,6 +2925,15 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -2752,12 +2943,28 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
 [[package]]
 name = "smawk"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
 
+[[package]]
+name = "socket2"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "stacker"
 version = "0.1.15"
@@ -2805,7 +3012,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -2855,9 +3062,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.48"
+version = "2.0.58"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2913,7 +3120,7 @@ checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
 dependencies = [
  "cfg-if 1.0.0",
  "fastrand",
- "redox_syscall",
+ "redox_syscall 0.4.1",
  "rustix",
  "windows-sys 0.52.0",
 ]
@@ -2980,7 +3187,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -3046,6 +3253,49 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
+[[package]]
+name = "tokio"
+version = "1.38.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio 0.8.11",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.58",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
 [[package]]
 name = "toml"
 version = "0.5.11"
@@ -3200,7 +3450,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
  "wasm-bindgen-shared",
 ]
 
@@ -3222,7 +3472,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -3478,7 +3728,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
 
 [[package]]
@@ -3498,5 +3748,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.58",
 ]
diff --git a/saffron/.gitignore b/saffron/.gitignore
new file mode 100644
index 0000000000..402fd5d873
--- /dev/null
+++ b/saffron/.gitignore
@@ -0,0 +1 @@
+proptest-regressions
diff --git a/saffron/Cargo.toml b/saffron/Cargo.toml
index c3b10fb3d1..2ad0b56cd9 100644
--- a/saffron/Cargo.toml
+++ b/saffron/Cargo.toml
@@ -17,12 +17,19 @@ name = "saffron"
 path = "src/main.rs"
 
 [dependencies]
+anyhow = "1.0"
 ark-ff.workspace = true
+ark-serialize.workspace = true
+bytes = "1.0"
 clap = { workspace = true, features = ["derive"] }
 mina-curves.workspace = true
-num-bigint.workspace = true
+o1-utils.workspace = true
+futures = "0.3"
+serde.workspace = true
+tokio = { version = "1", features = ["full"] }
+tokio-util = { version = "0.7", features = ["io"] }
+
 
 [dev-dependencies]
-proptest.workspace = true
-num-bigint = {workspace = true, features = ["arbitrary"]}
-ark-std.workspace = true
\ No newline at end of file
+ark-std.workspace = true
+proptest.workspace = true
\ No newline at end of file
diff --git a/saffron/fixtures/.gitignore b/saffron/fixtures/.gitignore
new file mode 100644
index 0000000000..da2818c571
--- /dev/null
+++ b/saffron/fixtures/.gitignore
@@ -0,0 +1 @@
+lorem-output.txt
diff --git a/saffron/fixtures/lorem-bin b/saffron/fixtures/lorem-bin
new file mode 100644
index 0000000000000000000000000000000000000000..dbb682d2dee20342c44a413087161c088b30d03a
GIT binary patch
literal 488
zcmY+BF>*s83`D8Z<O+KL&p$=drOg2pkR8nc!XSBkd<#2GeuLm^cAr-7>GgHZI2kvB
z$->mC1<ltGG@kmE3JS02kjl7p$xDV5!gOQHyb{b|-HQ~-T|!_qHWY2kg`o)t`Bf@`
z`h^-ob2}LC>W6CTSmIZl8cF9HnsjLhgD4pGouUVt8`bLQDn=%G@|{t9+S1fo3_y%{
z86mWtf=HHZ;TirseG1r|w$ld=3m{&5ZY`RISG^&~O(vJlzdQ%e{qQ7BPtaE=wMquB
z6<m?VEi}oJ4bJyZ91#e~q=8M^5toaf;i>Ds7!?_wqsmM<Yecrwc1~x-o}WgI-y5U*
nj=OUaode-tVinT&ernaW8zwI<Ajxw6+v30D?P&fC7q-#^W5}f<

literal 0
HcmV?d00001

diff --git a/saffron/fixtures/lorem.txt b/saffron/fixtures/lorem.txt
new file mode 100644
index 0000000000..1b376877f4
--- /dev/null
+++ b/saffron/fixtures/lorem.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/saffron/lorem.txt b/saffron/lorem.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7321ef9415c1d7eb64092c24d82266331562f5d3
GIT binary patch
literal 465
zcmZXQJyJt441}pU#U3E@P5~V)2N1RrcE*3Pr3}YM=0Sxk+mcqh-&>zr!Lhs|_tGa8
z4N}!ENWCphrsflpIk*zr4O$q*r8x$m^i_L?X&o`lmYA8n8Qf6hBjllWN-L_|nm`Kg
zE(hKX+E_yymHYCDr_>AX&V|;OP0!FTorxyK-r%K3P5o5fO<dfYek3`f87g7ChNpf8
zh*fsrWlM^ig44s(nUxJw$J9@4S!V=~k4G;)Vx5MaND5s`kyy$fp$O!-`i){}+YpA#
zc=$Z<`jvE;2C&6o&{Gmk(U*KMOQ=SyhA}&{woP%9ilxcRkewU-dS#*@>(aTawU=!}
RHXsI$^~=@k@%;4vd;^A?r4s-E

literal 0
HcmV?d00001

diff --git a/saffron/proptest-regressions/serialization.txt b/saffron/proptest-regressions/serialization.txt
deleted file mode 100644
index bb63fed4f0..0000000000
--- a/saffron/proptest-regressions/serialization.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Seeds for failure cases proptest has generated in the past. It is
-# automatically read and these particular cases re-run before any
-# novel cases are generated.
-#
-# It is recommended to check this file in to source control so that
-# everyone who runs the test benefits from these saved cases.
-cc cc009231a66ff36398e931c9374d5f97750d5af78e8df38a905f079981879b3d # shrinks to bs = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
diff --git a/saffron/src/cli.rs b/saffron/src/cli.rs
new file mode 100644
index 0000000000..761567f9d7
--- /dev/null
+++ b/saffron/src/cli.rs
@@ -0,0 +1,50 @@
+use clap::{arg, Parser};
+
+#[derive(Parser, Debug, Clone)]
+pub struct EncodeFileArgs {
+    #[arg(long, short = 'i', value_name = "FILE", help = "input file")]
+    pub input: String,
+
+    #[arg(
+        long,
+        short = 'o',
+        value_name = "FILE",
+        help = "output file (encoded as field elements)"
+    )]
+    pub output: String,
+}
+
+#[derive(Parser, Debug, Clone)]
+pub struct DecodeFileArgs {
+    #[arg(
+        long,
+        short = 'i',
+        value_name = "FILE",
+        help = "input file (encoded as field elements)"
+    )]
+    pub input: String,
+
+    #[arg(long, short = 'o', value_name = "FILE", help = "output file")]
+    pub output: String,
+
+    #[arg(
+        long,
+        short = 'n',
+        value_name = "NUM",
+        help = "number of bytes to decode"
+    )]
+    pub length: usize
+}
+
+#[derive(Parser, Debug, Clone)]
+#[command(
+    name = "saffron",
+    version = "0.1",
+    about = "saffron - a data availability layer"
+)]
+pub enum Commands {
+    #[command(name = "encode")]
+    Encode(EncodeFileArgs),
+    #[command(name = "decode")]
+    Decode(DecodeFileArgs),
+}
diff --git a/saffron/src/lib.rs b/saffron/src/lib.rs
index 16561bfd8d..29994c6126 100644
--- a/saffron/src/lib.rs
+++ b/saffron/src/lib.rs
@@ -1 +1,3 @@
 pub mod serialization;
+
+pub mod cli;
diff --git a/saffron/src/main.rs b/saffron/src/main.rs
index 425059571a..e31c9dbec0 100644
--- a/saffron/src/main.rs
+++ b/saffron/src/main.rs
@@ -1,7 +1,50 @@
-use saffron::serialization::from_bytes;
+use anyhow::Result;
+use clap::Parser;
+use mina_curves::pasta::Fp;
+use saffron::cli;
+use std::{
+    fs::File,
+    io::{Read, Write},
+};
 
-fn main() {
-    let bs = vec![5u8];
-    let n = from_bytes(&bs);
-    println!("Hello, world {}!", n);
+fn decode_file(args: cli::DecodeFileArgs) -> Result<()> {
+    let mut file = File::open(args.input)?;
+    let mut buf = Vec::new();
+    file.read_to_end(&mut buf)?;
+    let xs = saffron::serialization::deserialize_vec::<Fp>(&buf);
+    let bytes: Vec<u8> = xs
+        .into_iter()
+        .flat_map(|x| { 
+            saffron::serialization::decode(x).as_slice()[1..32].to_vec()
+         })
+        .collect();
+    let mut writer = File::create(args.output)?;
+    writer.write_all(&bytes)?;
+    Ok(())
+}
+
+fn encode_file(args: cli::EncodeFileArgs) -> Result<()> {
+    let mut file = File::open(args.input)?;
+    let mut buf = Vec::new();
+    file.read_to_end(&mut buf)?;
+    let xs = buf
+        .chunks(31)
+        .map(|chunk| {
+            let mut bytes = [0u8; 31];
+            bytes[..chunk.len()].copy_from_slice(chunk);
+            saffron::serialization::encode(&bytes)
+        })
+        .collect::<Vec<Fp>>();
+    let bytes = saffron::serialization::serialize_vec(&xs);
+    let mut writer = File::create(args.output)?;
+    writer.write_all(&bytes)?;
+    Ok(())
+}
+
+pub fn main() -> Result<()> {
+    let args = cli::Commands::parse();
+    match args {
+        cli::Commands::Encode(args) => encode_file(args),
+        cli::Commands::Decode(args) => decode_file(args),
+    }
 }
diff --git a/saffron/src/serialization.rs b/saffron/src/serialization.rs
index 3d2e7e0b61..e1f46f0e19 100644
--- a/saffron/src/serialization.rs
+++ b/saffron/src/serialization.rs
@@ -1,39 +1,67 @@
-use mina_curves::pasta::Fp;
-use num_bigint::BigUint;
+use ark_ff::{BigInteger, Field, PrimeField};
+use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress};
 
-pub fn from_bytes(bs: &[u8]) -> Fp {
-    BigUint::from_bytes_be(bs).into()
+// For injectivity, you can only use this on inputs of length at most
+// 'F::MODULUS_BIT_SIZE / 8', e.g. for Vesta this is 31.
+pub fn encode<Fp: PrimeField>(bytes: &[u8]) -> Fp {
+    Fp::from_be_bytes_mod_order(bytes)
 }
 
-pub fn to_bytes(f: Fp) -> Vec<u8> {
-    BigUint::from(f).to_bytes_be()
+pub fn decode<Fp: PrimeField>(x: Fp) -> Vec<u8> {
+    x.into_bigint().to_bytes_be()
+}
+
+pub fn serialize_vec<F: Field>(xs: &[F]) -> Vec<u8> {
+    let n = xs.serialized_size(Compress::Yes);
+    let mut writer = Vec::with_capacity(n);
+    xs.serialize_compressed(&mut writer)
+        .expect("Failed to serialize field elements");
+    writer
+}
+
+pub fn deserialize_vec<F: PrimeField>(bytes: &[u8]) -> Vec<F> {
+    Vec::<F>::deserialize_compressed(bytes).expect("Failed to deserialize field elements")
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
+    use ark_std::UniformRand;
+    use mina_curves::pasta::Fp;
     use proptest::prelude::*;
 
     proptest! {
         #[test]
-        fn test_round_trip_from_bytes(bs in any::<[u8;31]>())
-          { let n = from_bytes(&bs);
-            let bs2 = to_bytes(n);
-            prop_assert_eq!(bs, bs2.as_slice());
+        fn test_round_trip_from_bytes(xs in any::<[u8;31]>())
+          { let n : Fp = encode(&xs);
+            let ys : [u8; 31] = decode(n).as_slice()[1..32].try_into().unwrap();
+            prop_assert_eq!(xs, ys);
           }
     }
 
-    use ark_std::UniformRand;
-
     proptest! {
         #[test]
         fn test_round_trip_from_fp(
-            // Generate a valid field element using the curve's RNG
-            a in prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
+            x in prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
+        ) {
+            let bytes = decode(x);
+            let y = encode(&bytes);
+            prop_assert_eq!(x,y);
+        }
+    }
+
+    fn fp_strategy() -> impl Strategy<Value = Fp> {
+        prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
+    }
+
+    proptest! {
+        #[test]
+        fn test_round_trip_vec(
+            xs in prop::collection::vec(fp_strategy(), 0..100)
         ) {
-            let bs = to_bytes(a);
-            let a2 = from_bytes(&bs);
-            prop_assert_eq!(a, a2);
+            let bytes = serialize_vec(&xs);
+            let ys = deserialize_vec(&bytes);
+            prop_assert_eq!(xs,ys);
         }
     }
 }

From 0ea239e47160e22ab0edcb3b929306949e23e1e4 Mon Sep 17 00:00:00 2001
From: martyall <martyall@protonmail.com>
Date: Wed, 15 Jan 2025 13:44:47 -0800
Subject: [PATCH 3/3] introduce FieldBlob type. use bash script for e2e test.
 implement round trip test as property-based-test

---
 Cargo.lock                   | 257 +----------------------------------
 saffron/.gitignore           |   2 +
 saffron/Cargo.toml           |   7 +-
 saffron/fixtures/.gitignore  |   1 -
 saffron/fixtures/lorem-bin   | Bin 488 -> 0 bytes
 saffron/lorem.txt            | Bin 465 -> 0 bytes
 saffron/src/cli.rs           |   8 --
 saffron/src/main.rs          |  28 ++--
 saffron/src/serialization.rs | 116 ++++++++++++----
 saffron/test-encoding.sh     |  15 ++
 10 files changed, 123 insertions(+), 311 deletions(-)
 delete mode 100644 saffron/fixtures/.gitignore
 delete mode 100644 saffron/fixtures/lorem-bin
 delete mode 100644 saffron/lorem.txt
 create mode 100755 saffron/test-encoding.sh

diff --git a/Cargo.lock b/Cargo.lock
index 4844e18284..c8f212d0ee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -491,12 +491,6 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
-[[package]]
-name = "bytes"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
-
 [[package]]
 name = "cargo-spec"
 version = "0.5.0"
@@ -1094,7 +1088,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "redox_syscall 0.4.1",
+ "redox_syscall",
  "windows-sys 0.52.0",
 ]
 
@@ -1187,95 +1181,6 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
 
-[[package]]
-name = "futures"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.58",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
-
-[[package]]
-name = "futures-task"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
-
-[[package]]
-name = "futures-util"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
 [[package]]
 name = "generic-array"
 version = "0.14.7"
@@ -1747,16 +1652,6 @@ version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
 [[package]]
 name = "log"
 version = "0.4.20"
@@ -1926,17 +1821,6 @@ dependencies = [
  "winapi 0.2.8",
 ]
 
-[[package]]
-name = "mio"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "mio-extras"
 version = "2.0.6"
@@ -1945,7 +1829,7 @@ checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
 dependencies = [
  "lazycell",
  "log",
- "mio 0.6.23",
+ "mio",
  "slab",
 ]
 
@@ -2019,7 +1903,7 @@ dependencies = [
  "fsevent-sys",
  "inotify",
  "libc",
- "mio 0.6.23",
+ "mio",
  "mio-extras",
  "walkdir",
  "winapi 0.3.9",
@@ -2069,16 +1953,6 @@ dependencies = [
  "libm",
 ]
 
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.4",
- "libc",
-]
-
 [[package]]
 name = "o1-utils"
 version = "0.1.0"
@@ -2287,29 +2161,6 @@ version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
 
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "redox_syscall 0.5.8",
- "smallvec",
- "windows-targets 0.52.0",
-]
-
 [[package]]
 name = "paste"
 version = "1.0.14"
@@ -2367,18 +2218,6 @@ dependencies = [
  "sha2",
 ]
 
-[[package]]
-name = "pin-project-lite"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
 [[package]]
 name = "pkg-config"
 version = "0.3.29"
@@ -2640,15 +2479,6 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
-[[package]]
-name = "redox_syscall"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
-dependencies = [
- "bitflags 2.4.2",
-]
-
 [[package]]
 name = "regex"
 version = "1.10.3"
@@ -2778,15 +2608,10 @@ dependencies = [
  "ark-ff",
  "ark-serialize",
  "ark-std",
- "bytes",
  "clap 4.4.18",
- "futures",
  "mina-curves",
  "o1-utils",
  "proptest",
- "serde",
- "tokio",
- "tokio-util",
 ]
 
 [[package]]
@@ -2798,12 +2623,6 @@ dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
 [[package]]
 name = "secp256k1"
 version = "0.28.2"
@@ -2925,15 +2744,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -2943,28 +2753,12 @@ dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-
 [[package]]
 name = "smawk"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
 
-[[package]]
-name = "socket2"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
 [[package]]
 name = "stacker"
 version = "0.1.15"
@@ -3120,7 +2914,7 @@ checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
 dependencies = [
  "cfg-if 1.0.0",
  "fastrand",
- "redox_syscall 0.4.1",
+ "redox_syscall",
  "rustix",
  "windows-sys 0.52.0",
 ]
@@ -3253,49 +3047,6 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
-[[package]]
-name = "tokio"
-version = "1.38.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
-dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio 0.8.11",
- "num_cpus",
- "parking_lot",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.58",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
 [[package]]
 name = "toml"
 version = "0.5.11"
diff --git a/saffron/.gitignore b/saffron/.gitignore
index 402fd5d873..99b9bbeebd 100644
--- a/saffron/.gitignore
+++ b/saffron/.gitignore
@@ -1 +1,3 @@
 proptest-regressions
+fixtures/lorem.bin
+fixtures/lorem-decoded.txt
diff --git a/saffron/Cargo.toml b/saffron/Cargo.toml
index 2ad0b56cd9..ef77f57cf7 100644
--- a/saffron/Cargo.toml
+++ b/saffron/Cargo.toml
@@ -19,15 +19,10 @@ path = "src/main.rs"
 [dependencies]
 anyhow = "1.0"
 ark-ff.workspace = true
-ark-serialize.workspace = true
-bytes = "1.0"
+ark-serialize = { workspace = true, features = ["derive"]}
 clap = { workspace = true, features = ["derive"] }
 mina-curves.workspace = true
 o1-utils.workspace = true
-futures = "0.3"
-serde.workspace = true
-tokio = { version = "1", features = ["full"] }
-tokio-util = { version = "0.7", features = ["io"] }
 
 
 [dev-dependencies]
diff --git a/saffron/fixtures/.gitignore b/saffron/fixtures/.gitignore
deleted file mode 100644
index da2818c571..0000000000
--- a/saffron/fixtures/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-lorem-output.txt
diff --git a/saffron/fixtures/lorem-bin b/saffron/fixtures/lorem-bin
deleted file mode 100644
index dbb682d2dee20342c44a413087161c088b30d03a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 488
zcmY+BF>*s83`D8Z<O+KL&p$=drOg2pkR8nc!XSBkd<#2GeuLm^cAr-7>GgHZI2kvB
z$->mC1<ltGG@kmE3JS02kjl7p$xDV5!gOQHyb{b|-HQ~-T|!_qHWY2kg`o)t`Bf@`
z`h^-ob2}LC>W6CTSmIZl8cF9HnsjLhgD4pGouUVt8`bLQDn=%G@|{t9+S1fo3_y%{
z86mWtf=HHZ;TirseG1r|w$ld=3m{&5ZY`RISG^&~O(vJlzdQ%e{qQ7BPtaE=wMquB
z6<m?VEi}oJ4bJyZ91#e~q=8M^5toaf;i>Ds7!?_wqsmM<Yecrwc1~x-o}WgI-y5U*
nj=OUaode-tVinT&ernaW8zwI<Ajxw6+v30D?P&fC7q-#^W5}f<

diff --git a/saffron/lorem.txt b/saffron/lorem.txt
deleted file mode 100644
index 7321ef9415c1d7eb64092c24d82266331562f5d3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 465
zcmZXQJyJt441}pU#U3E@P5~V)2N1RrcE*3Pr3}YM=0Sxk+mcqh-&>zr!Lhs|_tGa8
z4N}!ENWCphrsflpIk*zr4O$q*r8x$m^i_L?X&o`lmYA8n8Qf6hBjllWN-L_|nm`Kg
zE(hKX+E_yymHYCDr_>AX&V|;OP0!FTorxyK-r%K3P5o5fO<dfYek3`f87g7ChNpf8
zh*fsrWlM^ig44s(nUxJw$J9@4S!V=~k4G;)Vx5MaND5s`kyy$fp$O!-`i){}+YpA#
zc=$Z<`jvE;2C&6o&{Gmk(U*KMOQ=SyhA}&{woP%9ilxcRkewU-dS#*@>(aTawU=!}
RHXsI$^~=@k@%;4vd;^A?r4s-E

diff --git a/saffron/src/cli.rs b/saffron/src/cli.rs
index 761567f9d7..6f539136a3 100644
--- a/saffron/src/cli.rs
+++ b/saffron/src/cli.rs
@@ -26,14 +26,6 @@ pub struct DecodeFileArgs {
 
     #[arg(long, short = 'o', value_name = "FILE", help = "output file")]
     pub output: String,
-
-    #[arg(
-        long,
-        short = 'n',
-        value_name = "NUM",
-        help = "number of bytes to decode"
-    )]
-    pub length: usize
 }
 
 #[derive(Parser, Debug, Clone)]
diff --git a/saffron/src/main.rs b/saffron/src/main.rs
index e31c9dbec0..a8dfa711e6 100644
--- a/saffron/src/main.rs
+++ b/saffron/src/main.rs
@@ -1,7 +1,8 @@
 use anyhow::Result;
+use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
 use clap::Parser;
 use mina_curves::pasta::Fp;
-use saffron::cli;
+use saffron::{cli, serialization::FieldBlob};
 use std::{
     fs::File,
     io::{Read, Write},
@@ -11,15 +12,10 @@ fn decode_file(args: cli::DecodeFileArgs) -> Result<()> {
     let mut file = File::open(args.input)?;
     let mut buf = Vec::new();
     file.read_to_end(&mut buf)?;
-    let xs = saffron::serialization::deserialize_vec::<Fp>(&buf);
-    let bytes: Vec<u8> = xs
-        .into_iter()
-        .flat_map(|x| { 
-            saffron::serialization::decode(x).as_slice()[1..32].to_vec()
-         })
-        .collect();
+    let blob: FieldBlob<Fp> = FieldBlob::<Fp>::deserialize_compressed(&buf[..])?;
+    let data = FieldBlob::<Fp>::decode(blob);
     let mut writer = File::create(args.output)?;
-    writer.write_all(&bytes)?;
+    writer.write_all(&data)?;
     Ok(())
 }
 
@@ -27,17 +23,11 @@ fn encode_file(args: cli::EncodeFileArgs) -> Result<()> {
     let mut file = File::open(args.input)?;
     let mut buf = Vec::new();
     file.read_to_end(&mut buf)?;
-    let xs = buf
-        .chunks(31)
-        .map(|chunk| {
-            let mut bytes = [0u8; 31];
-            bytes[..chunk.len()].copy_from_slice(chunk);
-            saffron::serialization::encode(&bytes)
-        })
-        .collect::<Vec<Fp>>();
-    let bytes = saffron::serialization::serialize_vec(&xs);
+    let blob = FieldBlob::<Fp>::encode(&buf);
+    let mut bytes_to_write = Vec::with_capacity(buf.len());
+    blob.serialize_compressed(&mut bytes_to_write)?;
     let mut writer = File::create(args.output)?;
-    writer.write_all(&bytes)?;
+    writer.write_all(&bytes_to_write)?;
     Ok(())
 }
 
diff --git a/saffron/src/serialization.rs b/saffron/src/serialization.rs
index e1f46f0e19..292ccda8af 100644
--- a/saffron/src/serialization.rs
+++ b/saffron/src/serialization.rs
@@ -1,26 +1,91 @@
-use ark_ff::{BigInteger, Field, PrimeField};
-use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress};
+use ark_ff::{BigInteger, PrimeField};
+use ark_serialize::{
+    CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate,
+    Write,
+};
+use o1_utils::FieldHelpers;
 
 // For injectivity, you can only use this on inputs of length at most
 // 'F::MODULUS_BIT_SIZE / 8', e.g. for Vesta this is 31.
-pub fn encode<Fp: PrimeField>(bytes: &[u8]) -> Fp {
+fn encode<Fp: PrimeField>(bytes: &[u8]) -> Fp {
     Fp::from_be_bytes_mod_order(bytes)
 }
 
-pub fn decode<Fp: PrimeField>(x: Fp) -> Vec<u8> {
+fn decode<Fp: PrimeField>(x: Fp) -> Vec<u8> {
     x.into_bigint().to_bytes_be()
 }
 
-pub fn serialize_vec<F: Field>(xs: &[F]) -> Vec<u8> {
-    let n = xs.serialized_size(Compress::Yes);
-    let mut writer = Vec::with_capacity(n);
-    xs.serialize_compressed(&mut writer)
-        .expect("Failed to serialize field elements");
-    writer
+// A FieldBlob<F> represents the encoding of a Vec<u8> as a Vec<F> where F is a prime field.
+#[derive(Clone, Debug, PartialEq)]
+pub struct FieldBlob<F> {
+    pub n_bytes: usize,
+    pub data: Vec<F>,
 }
 
-pub fn deserialize_vec<F: PrimeField>(bytes: &[u8]) -> Vec<F> {
-    Vec::<F>::deserialize_compressed(bytes).expect("Failed to deserialize field elements")
+impl<F: CanonicalSerialize> CanonicalSerialize for FieldBlob<F> {
+    fn serialize_with_mode<W: Write>(
+        &self,
+        mut writer: W,
+        mode: Compress,
+    ) -> Result<(), SerializationError> {
+        self.n_bytes.serialize_with_mode(&mut writer, mode)?;
+        self.data.serialize_with_mode(&mut writer, mode)?;
+        Ok(())
+    }
+
+    fn serialized_size(&self, mode: Compress) -> usize {
+        self.n_bytes.serialized_size(mode) + self.data.serialized_size(mode)
+    }
+}
+
+impl<F: Valid> Valid for FieldBlob<F> {
+    fn check(&self) -> Result<(), SerializationError> {
+        self.n_bytes.check()?;
+        self.data.check()?;
+        Ok(())
+    }
+}
+
+impl<F: CanonicalDeserialize> CanonicalDeserialize for FieldBlob<F> {
+    fn deserialize_with_mode<R: Read>(
+        mut reader: R,
+        compress: Compress,
+        validate: Validate,
+    ) -> Result<Self, SerializationError> {
+        let n_bytes = usize::deserialize_with_mode(&mut reader, compress, validate)?;
+        let data = Vec::<F>::deserialize_with_mode(&mut reader, compress, validate)?;
+        Ok(Self { n_bytes, data })
+    }
+}
+
+impl<F: PrimeField> FieldBlob<F> {
+    // Encode a bytestring as a list of field elements.
+    pub fn encode(bytes: &[u8]) -> FieldBlob<F> {
+        let n = (F::MODULUS_BIT_SIZE / 8) as usize;
+        let data = bytes
+            .chunks(n)
+            .map(|chunk| {
+                let mut bytes = vec![0u8; n];
+                bytes[..chunk.len()].copy_from_slice(chunk);
+                encode(&bytes)
+            })
+            .collect::<Vec<F>>();
+        FieldBlob {
+            n_bytes: bytes.len(),
+            data,
+        }
+    }
+
+    // Decode a list of field elements as a bytestring.
+    pub fn decode(blob: FieldBlob<F>) -> Vec<u8> {
+        let n = (F::MODULUS_BIT_SIZE / 8) as usize;
+        let m = F::size_in_bytes();
+        blob.data
+            .into_iter()
+            .flat_map(|x| decode(x).as_slice()[(m - n)..m].to_vec())
+            .take(blob.n_bytes)
+            .collect()
+    }
 }
 
 #[cfg(test)]
@@ -30,6 +95,7 @@ mod tests {
     use mina_curves::pasta::Fp;
     use proptest::prelude::*;
 
+    // Check that [u8] -> Fp -> [u8] is the identity function.
     proptest! {
         #[test]
         fn test_round_trip_from_bytes(xs in any::<[u8;31]>())
@@ -39,6 +105,7 @@ mod tests {
           }
     }
 
+    // Check that Fp -> [u8] -> Fp is the identity function.
     proptest! {
         #[test]
         fn test_round_trip_from_fp(
@@ -50,18 +117,19 @@ mod tests {
         }
     }
 
-    fn fp_strategy() -> impl Strategy<Value = Fp> {
-        prop::strategy::Just(Fp::rand(&mut ark_std::rand::thread_rng()))
-    }
-
+    // check that Vec<u8> -> FieldBlob<Fp> -> Vec<u8> is the identity function
     proptest! {
-        #[test]
-        fn test_round_trip_vec(
-            xs in prop::collection::vec(fp_strategy(), 0..100)
-        ) {
-            let bytes = serialize_vec(&xs);
-            let ys = deserialize_vec(&bytes);
-            prop_assert_eq!(xs,ys);
-        }
+    #[test]
+    fn test_round_trip_blob_encoding( xs in any::<Vec<u8>>())
+      { let blob = FieldBlob::<Fp>::encode(&xs);
+        let mut buf = Vec::new();
+        blob.serialize_compressed(&mut buf).unwrap();
+        let a = FieldBlob::<Fp>::deserialize_compressed(&buf[..]).unwrap();
+        // check that ark-serialize is behaving as expected
+        prop_assert_eq!(blob.clone(), a);
+        let ys = FieldBlob::<Fp>::decode(blob);
+        // check that we get the byte blob back again
+        prop_assert_eq!(xs,ys);
+      }
     }
 }
diff --git a/saffron/test-encoding.sh b/saffron/test-encoding.sh
new file mode 100755
index 0000000000..23d1095ac2
--- /dev/null
+++ b/saffron/test-encoding.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Run encode and decode
+cargo run --bin saffron encode -i fixtures/lorem.txt -o fixtures/lorem.bin
+cargo run --bin saffron decode -i fixtures/lorem.bin -o fixtures/lorem-decoded.txt
+
+# Compare files
+if cmp -s fixtures/lorem.txt fixtures/lorem-decoded.txt; then
+    echo "Files are identical"
+    exit 0
+else
+    echo "Files differ"
+    diff fixtures/lorem.txt fixtures/lorem-decoded.txt
+    exit 1
+fi
\ No newline at end of file