From 148258b8fe9ba66e4895561753b38e10bafcbefe Mon Sep 17 00:00:00 2001 From: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:19:29 -0300 Subject: [PATCH] feat: implement basic transaction prover service (#881) --- CHANGELOG.md | 1 + Cargo.lock | 912 +++++++++++++++++++++- Cargo.toml | 3 +- Makefile | 11 +- {bench-tx => bin/bench-tx}/Cargo.toml | 0 {bench-tx => bin/bench-tx}/README.md | 0 {bench-tx => bin/bench-tx}/bench-tx.json | 0 {bench-tx => bin/bench-tx}/src/main.rs | 0 {bench-tx => bin/bench-tx}/src/utils.rs | 0 bin/tx-prover/Cargo.toml | 40 + bin/tx-prover/README.md | 54 ++ bin/tx-prover/build.rs | 46 ++ bin/tx-prover/proto/api.proto | 15 + bin/tx-prover/src/main.rs | 142 ++++ bin/tx-prover/src/server/generated/api.rs | 278 +++++++ bin/tx-prover/src/server/generated/mod.rs | 1 + bin/tx-prover/src/server/mod.rs | 87 +++ 17 files changed, 1579 insertions(+), 11 deletions(-) rename {bench-tx => bin/bench-tx}/Cargo.toml (100%) rename {bench-tx => bin/bench-tx}/README.md (100%) rename {bench-tx => bin/bench-tx}/bench-tx.json (100%) rename {bench-tx => bin/bench-tx}/src/main.rs (100%) rename {bench-tx => bin/bench-tx}/src/utils.rs (100%) create mode 100644 bin/tx-prover/Cargo.toml create mode 100644 bin/tx-prover/README.md create mode 100644 bin/tx-prover/build.rs create mode 100644 bin/tx-prover/proto/api.proto create mode 100644 bin/tx-prover/src/main.rs create mode 100644 bin/tx-prover/src/server/generated/api.rs create mode 100644 bin/tx-prover/src/server/generated/mod.rs create mode 100644 bin/tx-prover/src/server/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index eb19df2ac..dac580109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.6.0 (TBD) +- Created a proving service that receives `TransactionWitness` and returns the proof using gRPC (#881). - Made note scripts public (#880). - Implemented serialization for `TransactionWitness`, `ChainMmr`, `TransactionInputs` and `TransactionArgs` (#888). - [BREAKING] Renamed the `TransactionProver` struct to `LocalTransactionProver` and added the `TransactionProver` trait (#865). diff --git a/Cargo.lock b/Cargo.lock index 5478548bc..7eca96c6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,12 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + [[package]] name = "arrayref" version = "0.3.9" @@ -59,12 +65,145 @@ dependencies = [ "term", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower 0.4.13", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +dependencies = [ + "async-trait", + "axum-core 0.4.4", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower 0.5.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -89,6 +228,18 @@ dependencies = [ "backtrace", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bit-set" version = "0.5.3" @@ -104,6 +255,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -138,6 +295,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + [[package]] name = "cast" version = "0.3.0" @@ -383,6 +546,21 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.30" @@ -524,6 +702,25 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -534,24 +731,198 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.30", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "tokio", + "tower 0.4.13", + "tower-service", +] + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.5.0" @@ -559,7 +930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -568,7 +939,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] @@ -597,6 +968,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -672,7 +1052,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.6.0", "libc", ] @@ -698,6 +1078,39 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "logos" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1ceb190eb9bdeecdd8f1ad6a71d6d632a50905948771718741b5461fb01e13" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90be66cb7bd40cb5cc2e9cfaf2d1133b04a3d93b72344267715010a466e0915a" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax 0.8.4", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45154231e8e96586b39494029e58f12f8ffcb5ecf80333a603a13aa205ea8cbd" +dependencies = [ + "logos-codegen", +] + [[package]] name = "loom" version = "0.7.2" @@ -720,6 +1133,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -957,6 +1376,28 @@ dependencies = [ "winter-maybe-async 0.10.0", ] +[[package]] +name = "miden-tx-prover" +version = "0.6.0" +dependencies = [ + "axum 0.7.6", + "miden-lib", + "miden-objects", + "miden-prover", + "miden-tx", + "miette", + "prost", + "prost-build", + "protox", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", + "tonic-web", + "tracing", + "tracing-subscriber", +] + [[package]] name = "miden-verifier" version = "0.10.5" @@ -969,6 +1410,43 @@ dependencies = [ "winter-verifier", ] +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -978,6 +1456,24 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1135,6 +1631,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "petgraph" version = "0.6.5" @@ -1142,7 +1644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.5.0", ] [[package]] @@ -1154,6 +1656,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1181,6 +1703,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1199,6 +1731,99 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-reflect" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f" +dependencies = [ + "logos", + "miette", + "once_cell", + "prost", + "prost-types", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + +[[package]] +name = "protox" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac532509cee918d40f38c3e12f8ef9230f215f017d54de7dd975015538a42ce7" +dependencies = [ + "bytes", + "miette", + "prost", + "prost-reflect", + "prost-types", + "protox-parse", + "thiserror", +] + +[[package]] +name = "protox-parse" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6c33f43516fe397e2f930779d720ca12cd057f7da4cd6326a0ef78d69dee96" +dependencies = [ + "logos", + "miette", + "prost-types", + "thiserror", +] + [[package]] name = "quote" version = "1.0.37" @@ -1264,7 +1889,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -1388,7 +2013,7 @@ version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1475,13 +2100,23 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap", + "indexmap 2.5.0", "itoa", "memchr", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -1491,6 +2126,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha3" version = "0.10.8" @@ -1516,6 +2163,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[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 = "siphasher" version = "0.3.11" @@ -1543,6 +2199,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -1603,6 +2269,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "tempfile" version = "3.12.0" @@ -1706,6 +2384,69 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.19" @@ -1733,19 +2474,146 @@ version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64", + "bytes", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tonic-web" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3b0e1cedbf19fdfb78ef3d672cb9928e0a91a9cb4629cc0c916e8cff8aaaa1" +dependencies = [ + "base64", + "bytes", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "pin-project", + "tokio-stream", + "tonic", + "tower-http", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1783,6 +2651,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -1793,14 +2671,23 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "trybuild" version = "1.0.99" @@ -1894,6 +2781,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 70d77e61c..339215a60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,11 @@ [workspace] resolver = "2" members = [ - "bench-tx", "miden-lib", "miden-tx", "objects", + "bin/bench-tx", + "bin/tx-prover", ] [workspace.package] diff --git a/Makefile b/Makefile index ff12b5d88..79c638f6b 100644 --- a/Makefile +++ b/Makefile @@ -81,12 +81,12 @@ build: ## By default we should build in release mode .PHONY: build-no-std build-no-std: ## Build without the standard library - cargo build --no-default-features --target wasm32-unknown-unknown --workspace --exclude miden-bench-tx + cargo build --no-default-features --target wasm32-unknown-unknown --workspace --exclude miden-bench-tx --exclude miden-tx-prover .PHONY: build-no-std-testing build-no-std-testing: ## Build without the standard library. Includes the `testing` feature - cargo build --no-default-features --target wasm32-unknown-unknown --workspace --exclude miden-bench-tx --features testing + cargo build --no-default-features --target wasm32-unknown-unknown --workspace --exclude miden-bench-tx --exclude miden-tx-prover --features testing .PHONY: build-async @@ -99,3 +99,10 @@ build-async: ## Build with the `async` feature enabled (only libraries) .PHONY: bench-tx bench-tx: ## Run transaction benchmarks cargo run --bin bench-tx + + +# --- installing ---------------------------------------------------------------------------------- + +.PHONY: install-prover +install-prover: ## Installs prover + cargo install --path bin/tx-prover --locked diff --git a/bench-tx/Cargo.toml b/bin/bench-tx/Cargo.toml similarity index 100% rename from bench-tx/Cargo.toml rename to bin/bench-tx/Cargo.toml diff --git a/bench-tx/README.md b/bin/bench-tx/README.md similarity index 100% rename from bench-tx/README.md rename to bin/bench-tx/README.md diff --git a/bench-tx/bench-tx.json b/bin/bench-tx/bench-tx.json similarity index 100% rename from bench-tx/bench-tx.json rename to bin/bench-tx/bench-tx.json diff --git a/bench-tx/src/main.rs b/bin/bench-tx/src/main.rs similarity index 100% rename from bench-tx/src/main.rs rename to bin/bench-tx/src/main.rs diff --git a/bench-tx/src/utils.rs b/bin/bench-tx/src/utils.rs similarity index 100% rename from bench-tx/src/utils.rs rename to bin/bench-tx/src/utils.rs diff --git a/bin/tx-prover/Cargo.toml b/bin/tx-prover/Cargo.toml new file mode 100644 index 000000000..dc8f5dcc7 --- /dev/null +++ b/bin/tx-prover/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "miden-tx-prover" +version = "0.6.0" +description = "Miden rollup transaction prover" +readme = "README.md" +keywords = ["miden", "transaction"] +license.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true +edition.workspace = true + +[features] +testing = ["miden-objects/testing", "miden-lib/testing", "miden-tx/testing"] + +[dependencies] +axum = "0.7" +miden-lib = { workspace = true } +miden-objects = { workspace = true } +miden-prover = { workspace = true } +miden-tx = { workspace = true } +prost = "0.12" +tokio = { version = "1.38", features = ["full"] } +tokio-stream = { version = "0.1", features = [ "net" ]} +tonic = "0.11" +tonic-web = { version = "0.11" } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = ["fmt", "json", "env-filter"] } + +[build-dependencies] +miette = { version = "7.2", features = ["fancy"] } +prost = { version = "0.12" } +prost-build = { version = "0.12" } +protox = { version = "0.6" } +tonic-build = { version = "0.11" } + +[dev-dependencies] +tokio = { version = "1.38", features = ["full"] } +tonic = "0.11" diff --git a/bin/tx-prover/README.md b/bin/tx-prover/README.md new file mode 100644 index 000000000..135aba96d --- /dev/null +++ b/bin/tx-prover/README.md @@ -0,0 +1,54 @@ +# Miden transaction prover + +A service for generating Miden transaction proofs on-demand. + +## Installation + +Install the prover binary for production using `cargo`: + +```sh +cargo install miden-tx-prover --locked +``` + +This will install the latest official version of the prover. You can install a specific version using `--version `: + +```sh +cargo install miden-tx-prover --locked --version x.y.z +``` + +You can also use `cargo` to compile the prover service from the source code if for some reason you need a specific git revision. Note that since these aren't official releases we cannot provide much support for any issues you run into, so consider this for advanced users only. The incantation is a little different as you'll be targetting this repo instead: + +```sh +# Install from a specific branch +cargo install --locked --git https://github.com/0xPolygonMiden/miden-base miden-tx-prover --branch + +# Install a specific tag +cargo install --locked --git https://github.com/0xPolygonMiden/miden-base miden-tx-prover --tag + +# Install a specific git revision +cargo install --locked --git https://github.com/0xPolygonMiden/miden-base miden-tx-prover --rev +``` + +If you want to build the prover from a local version, you can run: + +```bash +make install-prover +``` + +This step will also generate the necessary protobuf-related files. + +### Running the Service + +Once installed, you can run the service with: + +```bash +RUST_LOG=info miden-tx-prover +``` + +By default, the server will start on `0.0.0.0:50051`. You can change this and the log level by setting the following environment variables: + +```bash +PROVER_SERVICE_HOST= +PROVER_SERVICE_PORT= +RUST_LOG= +``` diff --git a/bin/tx-prover/build.rs b/bin/tx-prover/build.rs new file mode 100644 index 000000000..09a9e5fec --- /dev/null +++ b/bin/tx-prover/build.rs @@ -0,0 +1,46 @@ +use std::{env, fs, path::PathBuf}; + +use miette::IntoDiagnostic; +use protox::prost::Message; + +/// Generates Rust protobuf bindings from .proto files. +fn main() -> miette::Result<()> { + compile_tonic_server_proto()?; + + Ok(()) +} + +fn compile_tonic_server_proto() -> miette::Result<()> { + let crate_root: PathBuf = + env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should be set").into(); + let dst_dir = crate_root.join("src").join("server").join("generated"); + + // Remove api.rs file if exists. + let _ = fs::remove_file(dst_dir.join("api.rs")).into_diagnostic(); + + // Compute the directory of the `proto` definitions + let proto_dir: PathBuf = crate_root.join("proto"); + + // Compute the compiler's target file path. + let out = env::var("OUT_DIR").into_diagnostic()?; + let file_descriptor_path = PathBuf::from(out).join("file_descriptor_set.bin"); + + // Compile the proto file for all servers APIs + let protos = &[proto_dir.join("api.proto")]; + + let includes = &[proto_dir]; + let file_descriptors = protox::compile(protos, includes)?; + fs::write(&file_descriptor_path, file_descriptors.encode_to_vec()).into_diagnostic()?; + + let prost_config = prost_build::Config::new(); + + // Generate the stub of the user facing server from its proto file + tonic_build::configure() + .file_descriptor_set_path(&file_descriptor_path) + .skip_protoc_run() + .out_dir(&dst_dir) + .compile_with_config(prost_config, protos, includes) + .into_diagnostic()?; + + Ok(()) +} diff --git a/bin/tx-prover/proto/api.proto b/bin/tx-prover/proto/api.proto new file mode 100644 index 000000000..4555b326f --- /dev/null +++ b/bin/tx-prover/proto/api.proto @@ -0,0 +1,15 @@ +// Specification of the user facing gRPC API. +syntax = "proto3"; +package api; + +service Api { + rpc ProveTransaction(ProveTransactionRequest) returns (ProveTransactionResponse) {} +} + +message ProveTransactionRequest { + bytes transaction_witness = 1; +} + +message ProveTransactionResponse { + bytes proven_transaction = 1; +} diff --git a/bin/tx-prover/src/main.rs b/bin/tx-prover/src/main.rs new file mode 100644 index 000000000..d1f855ef6 --- /dev/null +++ b/bin/tx-prover/src/main.rs @@ -0,0 +1,142 @@ +use std::env; + +use server::Rpc; +use tokio::net::TcpListener; +use tokio_stream::wrappers::TcpListenerStream; +use tracing::info; + +pub mod server; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Initialize tracing subscriber with default settings for console output + tracing_subscriber::fmt::init(); + + let host = env::var("PROVER_SERVICE_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); + let port = env::var("PROVER_SERVICE_PORT").unwrap_or_else(|_| "50051".to_string()); + let addr = format!("{}:{}", host, port); + + let rpc = Rpc::new(TcpListener::bind(addr).await?); + + info!("Server listening on {}", rpc.listener.local_addr()?); + + // build our application with a route + tonic::transport::Server::builder() + .accept_http1(true) + .add_service(tonic_web::enable(rpc.api_service)) + .serve_with_incoming(TcpListenerStream::new(rpc.listener)) + .await?; + + Ok(()) +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod test { + use std::time::Duration; + + use miden_lib::transaction::TransactionKernel; + use miden_objects::{ + accounts::account_id::testing::{ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_SENDER}, + assets::{Asset, FungibleAsset}, + notes::NoteType, + testing::account_code::DEFAULT_AUTH_SCRIPT, + transaction::{ProvenTransaction, TransactionScript, TransactionWitness}, + }; + use miden_tx::{ + testing::mock_chain::{Auth, MockChain}, + utils::Serializable, + }; + use server::{ + generated::api::{api_client::ApiClient, api_server::ApiServer, ProveTransactionRequest}, + RpcApi, + }; + use tokio::net::TcpListener; + use tonic::Request; + + use super::*; + + #[tokio::test(flavor = "multi_thread", worker_threads = 3)] + async fn test_prove_transaction() { + // Start the server in the background + let listener = TcpListener::bind("127.0.0.1:50052").await.unwrap(); + let api_service = ApiServer::new(RpcApi::default()); + + // Spawn the server as a background task + tokio::spawn(async move { + tonic::transport::Server::builder() + .accept_http1(true) + .add_service(tonic_web::enable(api_service)) + .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener)) + .await + .unwrap(); + }); + + // Give the server some time to start + tokio::time::sleep(Duration::from_secs(1)).await; + + // Set up a gRPC client to send the request + let mut client = ApiClient::connect("http://127.0.0.1:50052").await.unwrap(); + let mut client_2 = ApiClient::connect("http://127.0.0.1:50052").await.unwrap(); + + // Create a mock transaction to send to the server + let mut mock_chain = MockChain::new(); + let account = mock_chain.add_existing_wallet(Auth::BasicAuth, vec![]); + + let fungible_asset_1: Asset = + FungibleAsset::new(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN.try_into().unwrap(), 100) + .unwrap() + .into(); + let note_1 = mock_chain + .add_p2id_note( + ACCOUNT_ID_SENDER.try_into().unwrap(), + account.id(), + &[fungible_asset_1], + NoteType::Private, + ) + .unwrap(); + + let tx_script = + TransactionScript::compile(DEFAULT_AUTH_SCRIPT, vec![], TransactionKernel::assembler()) + .unwrap(); + let tx_context = mock_chain + .build_tx_context(account.id()) + .input_notes(vec![note_1]) + .tx_script(tx_script) + .build(); + + let executed_transaction = tx_context.execute().unwrap(); + + let transaction_witness = TransactionWitness::from(executed_transaction); + + let request_1 = Request::new(ProveTransactionRequest { + transaction_witness: transaction_witness.to_bytes(), + }); + + let request_2 = Request::new(ProveTransactionRequest { + transaction_witness: transaction_witness.to_bytes(), + }); + + // Send both requests concurrently + let (t1, t2) = ( + tokio::spawn(async move { client.prove_transaction(request_1).await }), + tokio::spawn(async move { client_2.prove_transaction(request_2).await }), + ); + + let (response_1, response_2) = (t1.await.unwrap(), t2.await.unwrap()); + + // Check the success response + assert!(response_1.is_ok() || response_2.is_ok()); + + // Check the failure response + assert!(response_1.is_err() || response_2.is_err()); + + let response_success = response_1.or(response_2).unwrap(); + + // Cast into a ProvenTransaction + let _proven_transaction: ProvenTransaction = + response_success.into_inner().try_into().expect("Failed to convert response"); + } +} diff --git a/bin/tx-prover/src/server/generated/api.rs b/bin/tx-prover/src/server/generated/api.rs new file mode 100644 index 000000000..9015ff32f --- /dev/null +++ b/bin/tx-prover/src/server/generated/api.rs @@ -0,0 +1,278 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProveTransactionRequest { + #[prost(bytes = "vec", tag = "1")] + pub transaction_witness: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProveTransactionResponse { + #[prost(bytes = "vec", tag = "1")] + pub proven_transaction: ::prost::alloc::vec::Vec, +} +/// Generated client implementations. +pub mod api_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::{http::Uri, *}; + #[derive(Debug, Clone)] + pub struct ApiClient { + inner: tonic::client::Grpc, + } + impl ApiClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ApiClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor(inner: T, interceptor: F) -> ApiClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + ApiClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + pub async fn prove_transaction( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/api.Api/ProveTransaction"); + let mut req = request.into_request(); + req.extensions_mut().insert(GrpcMethod::new("api.Api", "ProveTransaction")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +pub mod api_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ApiServer. + #[async_trait] + pub trait Api: Send + Sync + 'static { + async fn prove_transaction( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + } + #[derive(Debug)] + pub struct ApiServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl ApiServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for ApiServer + where + T: Api, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/api.Api/ProveTransaction" => { + #[allow(non_camel_case_types)] + struct ProveTransactionSvc(pub Arc); + impl tonic::server::UnaryService + for ProveTransactionSvc + { + type Response = super::ProveTransactionResponse; + type Future = BoxFuture, tonic::Status>; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = + async move { ::prove_transaction(&inner, request).await }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ProveTransactionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + }, + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), + } + } + } + impl Clone for ApiServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for ApiServer { + const NAME: &'static str = "api.Api"; + } +} diff --git a/bin/tx-prover/src/server/generated/mod.rs b/bin/tx-prover/src/server/generated/mod.rs new file mode 100644 index 000000000..e5fdf85ee --- /dev/null +++ b/bin/tx-prover/src/server/generated/mod.rs @@ -0,0 +1 @@ +pub mod api; diff --git a/bin/tx-prover/src/server/mod.rs b/bin/tx-prover/src/server/mod.rs new file mode 100644 index 000000000..daeb224f9 --- /dev/null +++ b/bin/tx-prover/src/server/mod.rs @@ -0,0 +1,87 @@ +use generated::api::{api_server, ProveTransactionRequest, ProveTransactionResponse}; +use miden_objects::transaction::{ProvenTransaction, TransactionWitness}; +use miden_prover::DeserializationError; +use miden_tx::{ + utils::{Deserializable, Serializable}, + LocalTransactionProver, TransactionProver, +}; +use tokio::{net::TcpListener, sync::Mutex}; +use tonic::{Request, Response, Status}; +use tracing::info; + +pub mod generated; + +pub struct Rpc { + pub api_service: api_server::ApiServer, + pub listener: TcpListener, +} + +impl Rpc { + pub fn new(listener: TcpListener) -> Self { + let api_service = api_server::ApiServer::new(RpcApi::default()); + Self { listener, api_service } + } +} + +#[derive(Default)] +pub struct RpcApi { + prover: Mutex, +} + +// We need to implement Send and Sync for the generated code to be able to use the prover in the +// shared context. +unsafe impl Send for RpcApi {} +unsafe impl Sync for RpcApi {} + +#[tonic::async_trait] +impl api_server::Api for RpcApi { + async fn prove_transaction( + &self, + request: Request, + ) -> Result, tonic::Status> { + info!("Received request to prove transaction"); + + // Try to acquire a permit without waiting + let prover = self + .prover + .try_lock() + .map_err(|_| Status::resource_exhausted("Server is busy handling another request"))?; + + let transaction_witness = + TransactionWitness::read_from_bytes(&request.get_ref().transaction_witness) + .map_err(invalid_argument)?; + + let proof = prover.prove(transaction_witness).map_err(internal_error)?; + + Ok(Response::new(ProveTransactionResponse { proven_transaction: proof.to_bytes() })) + } +} + +// CONVERSIONS +// ================================================================================================ + +impl From for ProveTransactionResponse { + fn from(value: ProvenTransaction) -> Self { + ProveTransactionResponse { proven_transaction: value.to_bytes() } + } +} + +impl TryFrom for ProvenTransaction { + type Error = DeserializationError; + + fn try_from(response: ProveTransactionResponse) -> Result { + ProvenTransaction::read_from_bytes(&response.proven_transaction) + } +} + +// UTILITIES +// ================================================================================================ + +/// Formats an error +fn internal_error(err: E) -> Status { + Status::internal(format!("{:?}", err)) +} + +fn invalid_argument(err: E) -> Status { + Status::invalid_argument(format!("{:?}", err)) +}