diff --git a/Dockerfile b/Dockerfile index 8c47724a4b..53717933ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,8 +45,8 @@ FROM wasm-base AS wasm-libs-builder # clang / lld used by soft-float wasm RUN apt-get update && \ apt-get install -y clang=1:14.0-55.7~deb12u1 lld=1:14.0-55.7~deb12u1 wabt - # pinned rust 1.80.1 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi + # pinned rust 1.81.0 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.81.0 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi COPY ./Makefile ./ COPY arbitrator/Cargo.* arbitrator/ COPY arbitrator/arbutil arbitrator/arbutil @@ -73,6 +73,7 @@ COPY ./arbcompress ./arbcompress COPY ./arbos ./arbos COPY ./arbstate ./arbstate COPY ./espressocrypto ./espressocrypto +COPY ./espressocryptowasm/ ./espressocryptowasm/ COPY ./arbutil ./arbutil COPY ./gethhook ./gethhook COPY ./blsSignatures ./blsSignatures @@ -96,7 +97,7 @@ COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upg COPY --from=contracts-builder workspace/.make/ .make/ RUN PATH="$PATH:/usr/local/go/bin" NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-wasm-bin -FROM rust:1.80.1-slim-bookworm AS prover-header-builder +FROM rust:1.81.0-slim-bookworm AS prover-header-builder WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -114,16 +115,18 @@ COPY arbitrator/jit arbitrator/jit COPY arbitrator/espresso-crypto-helper arbitrator/espresso-crypto-helper COPY arbitrator/stylus arbitrator/stylus COPY arbitrator/tools/wasmer arbitrator/tools/wasmer +COPY espressocrypto espressocrypto COPY --from=brotli-wasm-export / target/ COPY scripts/build-brotli.sh scripts/ COPY brotli brotli RUN apt-get update && apt-get install -y cmake RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-header +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-espresso-crypto-lib FROM scratch AS prover-header-export COPY --from=prover-header-builder /workspace/target/ / -FROM rust:1.80.1-slim-bookworm AS prover-builder +FROM rust:1.81.0-slim-bookworm AS prover-builder WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ diff --git a/Makefile b/Makefile index fe517a2e9b..65f62af230 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ precompiles = $(patsubst %,./solgen/generated/%.go, $(precompile_names)) output_root=target output_latest=$(output_root)/machines/latest -repo_dirs = arbos arbcompress arbnode arbutil arbstate cmd das precompiles solgen system_tests util validator wavmio +repo_dirs = arbos arbcompress arbnode arbutil arbstate cmd das espressocrypto precompiles solgen system_tests util validator wavmio go_source.go = $(wildcard $(patsubst %,%/*.go, $(repo_dirs)) $(patsubst %,%/*/*.go, $(repo_dirs))) go_source.s = $(wildcard $(patsubst %,%/*.s, $(repo_dirs)) $(patsubst %,%/*/*.s, $(repo_dirs))) go_source = $(go_source.go) $(go_source.s) @@ -153,7 +153,18 @@ stylus_test_read-return-data_src = $(call get_stylus_test_rust,read-return-data stylus_test_wasms = $(stylus_test_keccak_wasm) $(stylus_test_keccak-100_wasm) $(stylus_test_fallible_wasm) $(stylus_test_storage_wasm) $(stylus_test_multicall_wasm) $(stylus_test_log_wasm) $(stylus_test_create_wasm) $(stylus_test_math_wasm) $(stylus_test_sdk-storage_wasm) $(stylus_test_erc20_wasm) $(stylus_test_read-return-data_wasm) $(stylus_test_evm-data_wasm) $(stylus_test_bfs:.b=.wasm) stylus_benchmarks = $(wildcard $(stylus_dir)/*.toml $(stylus_dir)/src/*.rs) $(stylus_test_wasms) +espresso_crypto_dir = ./espressocrypto/lib/espresso-crypto-helper +espresso_crypto_files = $(wildcard $(espresso_crypto_dir)/*.toml $(espresso_crypto_dir)/src/*.rs) +espresso_crypto_lib = $(output_root)/lib/libespresso_crypto_helper.a + # user targets +.PHONY: build-espresso-crypto-lib +build-espresso-crypto-lib: $(espresso_crypto_lib) + +$(espresso_crypto_lib): $(DEP_PREDICATE) $(espresso_crypto_files) + mkdir -p `dirname $(espresso_crypto_lib)` + cargo build --release --manifest-path $(espresso_crypto_dir)/Cargo.toml + install $(espresso_crypto_dir)/target/release/libespresso_crypto_helper.a $@ .PHONY: push push: lint test-go .make/fmt @@ -169,7 +180,7 @@ build: $(patsubst %,$(output_root)/bin/%, nitro deploy relay daserver datool seq @printf $(done) .PHONY: build-node-deps -build-node-deps: $(go_source) build-prover-header build-prover-lib build-jit .make/solgen .make/cbrotli-lib +build-node-deps: $(go_source) build-prover-header build-prover-lib build-jit .make/solgen .make/cbrotli-lib build-espresso-crypto-lib .PHONY: test-go-deps test-go-deps: \ @@ -286,7 +297,9 @@ clean: @rm -rf contracts/build contracts/cache solgen/go/ @rm -f .make/* rm -rf brotli/buildfiles - # Ensure lib64 is a symlink to lib + cargo clean --manifest-path $(espresso_crypto_dir)/Cargo.toml + +# Ensure lib64 is a symlink to lib mkdir -p $(output_root)/lib ln -s lib $(output_root)/lib64 diff --git a/arbitrator/jit/Cargo.toml b/arbitrator/jit/Cargo.toml index 4c71a6f3e1..a157fd832c 100644 --- a/arbitrator/jit/Cargo.toml +++ b/arbitrator/jit/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] arbutil = { path = "../arbutil/" } -espresso-crypto-helper = { path = "../espresso-crypto-helper/" } +espresso-crypto-helper = { path = "../espresso-crypto-helper/"} brotli = { path = "../brotli/", features = ["wasmer_traits"] } caller-env = { path = "../caller-env/", features = ["wasmer_traits"] } prover = { path = "../prover/", default-features = false, features = ["native"] } diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index d0ee53a517..1cf7bc6edc 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -593,6 +593,7 @@ func (b *BatchPoster) addEspressoBlockMerkleProof( } var newMsg arbostypes.L1IncomingMessage jst.BlockMerkleJustification = &arbostypes.BlockMerkleJustification{BlockMerkleProof: &proof, BlockMerkleComm: nextHeader.BlockMerkleTreeRoot} + if arbos.IsEspressoSovereignMsg(msg.Message) { // Passing an empty byte slice as payloadSignature because txs[0] already contains the payloadSignature here newMsg, err = arbos.MessageFromEspressoSovereignTx(txs[0], jst, []byte{}, msg.Message.Header) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index fcf7005079..4e95183e71 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -33,7 +33,7 @@ import ( "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/das/dastree" - "github.com/offchainlabs/nitro/espressocrypto" + "github.com/offchainlabs/nitro/espressocryptowasm" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/wavmio" ) @@ -322,14 +322,14 @@ func main() { if err != nil { panic("unable to serialize header") } - espressocrypto.VerifyMerkleProof( + espressocryptowasm.VerifyMerkleProof( jst.BlockMerkleJustification.BlockMerkleProof.Proof, jsonHeader, *jst.BlockMerkleJustification.BlockMerkleComm, commitment, ) if jst.Proof != nil { - espressocrypto.VerifyNamespace(chainConfig.ChainID.Uint64(), *jst.Proof, *jst.Header.PayloadCommitment, *jst.Header.NsTable, txs, *jst.VidCommon) + espressocryptowasm.VerifyNamespace(chainConfig.ChainID.Uint64(), *jst.Proof, *jst.Header.PayloadCommitment, *jst.Header.NsTable, txs, *jst.VidCommon) } } diff --git a/espressocrypto/espressocrypto_common.go b/espressocrypto/espressocrypto_common.go index c4495d5e46..b5f700cdfa 100644 --- a/espressocrypto/espressocrypto_common.go +++ b/espressocrypto/espressocrypto_common.go @@ -32,12 +32,12 @@ func VerifyNamespace( ns_table espressoTypes.NsTable, txs []espressoTypes.Bytes, common_data json.RawMessage, -) { +) bool { // TODO: this code will likely no longer be used in the STF soon. // G115: integer overflow conversion uint64 -> uint32 (gosec) // #nosec G115 var txnComm = hashTxns(uint32(namespace), txs) - verifyNamespace( + res := verifyNamespace( namespace, proof, []byte(block_comm.String()), @@ -45,6 +45,7 @@ func VerifyNamespace( []byte(txnComm), common_data, ) + return res } func VerifyMerkleProof( @@ -52,6 +53,6 @@ func VerifyMerkleProof( header json.RawMessage, blockComm espressoTypes.TaggedBase64, circuit_comm_bytes espressoTypes.Commitment, -) { - verifyMerkleProof(proof, header, []byte(blockComm.String()), circuit_comm_bytes[:]) +) bool { + return verifyMerkleProof(proof, header, []byte(blockComm.String()), circuit_comm_bytes[:]) } diff --git a/espressocrypto/lib/espresso-crypto-helper/Cargo.lock b/espressocrypto/lib/espresso-crypto-helper/Cargo.lock new file mode 100644 index 0000000000..078584aa95 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/Cargo.lock @@ -0,0 +1,2836 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "anyhow" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" + +[[package]] +name = "arbitrary" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775a8770d29db3dadcb858482cc240af7b2ffde4ac4de67d1d4955728103f0e2" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bw6-761" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0605daf0cc5aa2034b78d008aaf159f56901d92a52ee4f6ecdfdac4f426700" +dependencies = [ + "ark-bls12-377", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", + "blake2", + "derivative", + "digest", + "sha2", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ed-on-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10d901b9ac4b38f9c32beacedfadcdd64e46f8d7f8e88c1ae1060022cf6f6c6" +dependencies = [ + "ark-bls12-377", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ed-on-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6d678bb98a7e4f825bd4e332e93ac4f5a114ce2e3340dee4d7dc1c7ab5b323" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ed-on-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71892f265d01650e34988a546b37ea1d2ba1da162a639597a03d1550f26004d8" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "rayon", +] + +[[package]] +name = "ark-poly-commit" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a741492629ffcd228337676dc223a28551aa6792eedb8a2a22c767f00df6c89" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "rayon", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff", + "ark-std", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-srs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6e9a7036d369a637b2b9f871bc06cefe52a947c98e898dbafab26548f8aa22" +dependencies = [ + "anyhow", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-poly-commit", + "ark-serialize", + "ark-std", + "directories", + "hex-literal", + "rand", + "sha2", + "tracing", + "tracing-subscriber 0.3.18", + "ureq", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-bytes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce54e4e485fa0eed9c3aa5348162be09168f75bb5be7bc6587bcf2a65ee1386" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +dependencies = [ + "serde", +] + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + +[[package]] +name = "cc" +version = "1.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + +[[package]] +name = "committable" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a8809c2761232ce27226ef1ca1bc78b480b558406895848f76ab8fce04076c" +dependencies = [ + "arbitrary", + "ark-serialize", + "bitvec", + "derivative", + "derive_more 0.99.18", + "funty", + "hex", + "serde", + "sha3", + "tagged-base64", +] + +[[package]] +name = "const-hex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d475dfebcb4854d596b17b09f477616f80f17a550517f2b3615d8c205d5c802b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.87", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "espresso-crypto-helper" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-ed-on-bn254", + "ark-ff", + "ark-serialize", + "ark-srs", + "ark-std", + "base64", + "base64-bytes", + "bytesize", + "committable", + "derivative", + "derive_more 0.99.18", + "digest", + "either", + "ethereum-types", + "ethers-core", + "itertools 0.12.1", + "jf-crhf", + "jf-merkle-tree", + "jf-pcs", + "jf-rescue", + "jf-utils", + "jf-vid", + "lazy_static", + "libc", + "num-traits", + "paste", + "serde", + "serde_json", + "sha2", + "tagged-base64", + "trait-set", + "typenum", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "ethers-core" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" +dependencies = [ + "arrayvec", + "bytes", + "chrono", + "const-hex", + "elliptic-curve", + "ethabi", + "generic-array", + "k256", + "num_enum", + "open-fastrlp", + "rand", + "rlp", + "serde", + "serde_json", + "strum", + "tempfile", + "thiserror", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[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 = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde", + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.1", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jf-commitment" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-std", +] + +[[package]] +name = "jf-crhf" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-serialize", + "ark-std", +] + +[[package]] +name = "jf-merkle-tree" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "displaydoc", + "hashbrown 0.14.5", + "itertools 0.12.1", + "jf-relation", + "jf-rescue", + "jf-utils", + "num-bigint", + "num-traits", + "serde", + "sha3", + "tagged-base64", +] + +[[package]] +name = "jf-pcs" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "displaydoc", + "itertools 0.12.1", + "jf-utils", + "merlin", +] + +[[package]] +name = "jf-prf" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-serialize", + "ark-std", +] + +[[package]] +name = "jf-relation" +version = "0.4.4" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-bn254", + "ark-bw6-761", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "displaydoc", + "downcast-rs", + "dyn-clone", + "hashbrown 0.14.5", + "itertools 0.12.1", + "jf-utils", + "num-bigint", + "rand_chacha", +] + +[[package]] +name = "jf-rescue" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-bls12-377", + "ark-bls12-381", + "ark-bn254", + "ark-bw6-761", + "ark-crypto-primitives", + "ark-ed-on-bls12-377", + "ark-ed-on-bls12-381", + "ark-ed-on-bn254", + "ark-ff", + "ark-std", + "displaydoc", + "itertools 0.12.1", + "jf-commitment", + "jf-crhf", + "jf-prf", + "jf-relation", + "jf-utils", +] + +[[package]] +name = "jf-utils" +version = "0.4.4" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "ark-ec", + "ark-ed-on-bls12-377", + "ark-ed-on-bls12-381", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "digest", + "displaydoc", + "rand_chacha", + "serde", + "sha2", + "tagged-base64", +] + +[[package]] +name = "jf-vid" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=0.4.5#7d71dbeff14f1a501b0b0dc391f1dffa1b8374fb" +dependencies = [ + "anyhow", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "displaydoc", + "generic-array", + "itertools 0.12.1", + "jf-merkle-tree", + "jf-pcs", + "jf-utils", + "serde", + "tagged-base64", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", + "ethereum-types", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scale-info" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" +dependencies = [ + "cfg-if", + "derive_more 1.0.0", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.87", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tagged-base64" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b74bbf1db405a3fd2c6f8cd403bfa14727faa145925efe3012fa270b61551f1" +dependencies = [ + "ark-serialize", + "ark-std", + "base64", + "crc-any", + "serde", + "snafu", + "tagged-base64-macros", + "wasm-bindgen", +] + +[[package]] +name = "tagged-base64-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdea3c0150898fad2f7196a173ec3264f7fc455ba570939163c558f48ae85ac" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[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", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trait-set" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] diff --git a/espressocrypto/lib/espresso-crypto-helper/Cargo.toml b/espressocrypto/lib/espresso-crypto-helper/Cargo.toml new file mode 100644 index 0000000000..5a2116c86b --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/Cargo.toml @@ -0,0 +1,58 @@ + +[package] +name = "espresso-crypto-helper" +version = "0.1.0" +edition = "2021" +rust-version = "1.81" + +[lib] + +crate-type = ["staticlib"] + +[dependencies] +ark-bn254 = "0.4" +ark-ed-on-bn254 = "0.4" +ark-ff = "0.4" +ark-serialize = { version = "0.4", features = ["derive"] } +ark-std = { version = "0.4", default-features = false } +base64 = "0.22" +bytesize = "1.3" +derive_more = "0.99.17" +digest = "0.10" +ethers-core = "2.0.14" +libc = "0.2" +sha2 = "0.10" +base64-bytes = "0.1" +paste = "1.0" +serde_json = "1.0" +committable = {version = "0.2", features = ["ark-serialize"]} +ethereum-types = "0.14.1" +tagged-base64 = "0.4" +lazy_static = "1.4" +serde = { version = "1.0.195", features = ["derive"] } +itertools = "0.12" +either = {version = "1.11.0", features = ["serde"]} + +jf-crhf = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5" } +jf-utils = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [], default-features = false} +jf-vid = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", +], default-features = false} +jf-merkle-tree = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", +], default-features = false} +jf-pcs = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", +], default-features = false } +jf-rescue = { version = "0.1.0", git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ + "std", +], default-features = false} +trait-set = "0.3.0" +typenum = { version = "1.15.0", default-features = false, features = [ + "no_std", +] } +num-traits = "0.2.17" +derivative = "2.2" + +[dev-dependencies] +ark-srs = { version = "0.3.1" } diff --git a/espressocrypto/lib/espresso-crypto-helper/src/bytes.rs b/espressocrypto/lib/espresso-crypto-helper/src/bytes.rs new file mode 100644 index 0000000000..5859c871d7 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/bytes.rs @@ -0,0 +1,160 @@ +//! Convenient serialization for binary blobs. + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use derive_more::{From, Into}; +use serde::{ + de::{Deserialize, Deserializer, Error}, + ser::{Serialize, Serializer}, +}; +use std::{ + ops::{Deref, DerefMut}, + slice::SliceIndex, +}; + +/// An unstructured byte array with smart serialization. +/// +/// [`Bytes`] mostly acts as a simple byte array, `Vec`. It can easily be converted to and from +/// a `Vec`, and it implements many of the same traits as [`Vec`]. In fact internally it merely +/// wraps a `Vec`. +/// +/// The only difference is in how it serializes. `Vec` serializes very efficiently using +/// `bincode`, but using `serde_json`, it serializes as a JSON array of integers, which is +/// unconventional and inefficient. It is better, in JSON, to serialize binary data as a +/// base64-encoded string. [`Bytes`] uses the [`is_human_readable`](Serializer::is_human_readable) +/// property of a [`Serializer`] to detect whether we are serializing for a compact binary format +/// (like `bincode`) or a human-readable format (like JSON). In the former cases, it serializes +/// directly as an array of bytes. In the latter case, it serializes as a string using base 64. +#[derive( + Clone, + Debug, + Default, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + From, + Into, + CanonicalSerialize, + CanonicalDeserialize, +)] +pub struct Bytes(Vec); + +impl Bytes { + pub fn get(&self, index: I) -> Option<&I::Output> + where + I: SliceIndex<[u8]>, + { + self.0.get(index) + } +} + +impl Serialize for Bytes { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + BASE64.encode(self).serialize(s) + } else { + self.0.serialize(s) + } + } +} + +impl<'a> Deserialize<'a> for Bytes { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + Ok(Self(BASE64.decode(String::deserialize(d)?).map_err( + |err| D::Error::custom(format!("invalid base64: {err}")), + )?)) + } else { + Ok(Self(Vec::deserialize(d)?)) + } + } +} + +impl From<&[u8]> for Bytes { + fn from(bytes: &[u8]) -> Self { + Self(bytes.into()) + } +} + +impl From<[u8; N]> for Bytes { + fn from(bytes: [u8; N]) -> Self { + Self(bytes.into()) + } +} + +impl From<&[u8; N]> for Bytes { + fn from(bytes: &[u8; N]) -> Self { + Self((*bytes).into()) + } +} + +impl FromIterator for Bytes { + fn from_iter>(iter: I) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsMut<[u8]> for Bytes { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +impl Deref for Bytes { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.as_ref() + } +} + +impl DerefMut for Bytes { + fn deref_mut(&mut self) -> &mut [u8] { + self.as_mut() + } +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_ref() == other + } +} + +impl PartialEq<[u8; N]> for Bytes { + fn eq(&self, other: &[u8; N]) -> bool { + self.as_ref() == other + } +} + +impl PartialEq> for Bytes { + fn eq(&self, other: &Vec) -> bool { + self.0 == *other + } +} + +impl Extend for Bytes +where + Vec: Extend, +{ + fn extend>(&mut self, iter: I) { + self.0.extend(iter); + } +} + +#[macro_export] +macro_rules! bytes { + [$($elem:expr),* $(,)?] => { + $crate::bytes::Bytes::from(vec![$($elem),*]) + }; + [$elem:expr; $size:expr] => { + $crate::bytes::Bytes::from(vec![$elem; $size]) + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/full_payload.rs b/espressocrypto/lib/espresso-crypto-helper/src/full_payload.rs new file mode 100644 index 0000000000..dba15e03c3 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/full_payload.rs @@ -0,0 +1,8 @@ +mod ns_proof; +mod ns_table; +mod payload; + +pub use ns_proof::NsProof; +pub use ns_table::{NsIndex, NsTable}; + +pub use payload::PayloadByteLen; diff --git a/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_proof.rs b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_proof.rs new file mode 100644 index 0000000000..a263ca935d --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_proof.rs @@ -0,0 +1,87 @@ +use crate::hotshot_types::{ + vid_scheme, LargeRangeProofType, VidCommitment, VidCommon, VidSchemeType, +}; +use crate::{ + full_payload::{NsIndex, NsTable, PayloadByteLen}, + namespace_payload::NsPayloadOwned, + NamespaceId, Transaction, +}; +use jf_vid::{ + payload_prover::{PayloadProver, Statement}, + VidScheme, +}; +use serde::{Deserialize, Serialize}; + +/// Proof of correctness for namespace payload bytes in a block. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct NsProof { + ns_index: NsIndex, + ns_payload: NsPayloadOwned, + ns_proof: Option, // `None` if ns_payload is empty +} + +impl NsProof { + /// Verify a [`NsProof`] against a payload commitment. Returns `None` on + /// error or if verification fails. + /// + /// There is no [`NsPayload`](crate::block::namespace_payload::NsPayload) + /// arg because this data is already included in the [`NsProof`]. See + /// [`NsProof::new`] for discussion. + /// + /// If verification is successful then return `(Vec, + /// NamespaceId)` obtained by post-processing the underlying + /// [`NsPayload`](crate::block::namespace_payload::NsPayload). Why? This + /// method might be run by a client in a WASM environment who might be + /// running non-Rust code, in which case the client is unable to perform + /// this post-processing himself. + pub fn verify( + &self, + ns_table: &NsTable, + commit: &VidCommitment, + common: &VidCommon, + ) -> Option<(Vec, NamespaceId)> { + VidSchemeType::is_consistent(commit, common).ok()?; + if !ns_table.in_bounds(&self.ns_index) { + return None; // error: index out of bounds + } + + let range = ns_table + .ns_range(&self.ns_index, &PayloadByteLen::from_vid_common(common)) + .as_block_range(); + + match (&self.ns_proof, range.is_empty()) { + (Some(proof), false) => { + // TODO vid_scheme() arg should be u32 to match get_num_storage_nodes + // https://github.com/EspressoSystems/HotShot/issues/3298 + let vid = vid_scheme( + VidSchemeType::get_num_storage_nodes(common) + .try_into() + .ok()?, // error: failure to convert u32 to usize + ); + + vid.payload_verify( + Statement { + payload_subslice: self.ns_payload.as_bytes_slice(), + range, + commit, + common, + }, + proof, + ) + .ok()? // error: internal to payload_verify() + .ok()?; // verification failure + } + (None, true) => {} // 0-length namespace, nothing to verify + (None, false) => { + return None; + } + (Some(_), true) => { + return None; + } + } + + // verification succeeded, return some data + let ns_id = ns_table.read_ns_id_unchecked(&self.ns_index); + Some((self.ns_payload.export_all_txs(&ns_id), ns_id)) + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_table.rs b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_table.rs new file mode 100644 index 0000000000..c6fae5ce80 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/ns_table.rs @@ -0,0 +1,277 @@ +//! Types related to a namespace table. +//! +//! All code that needs to know the binary format of a namespace table is +//! restricted to this file. +//! +//! See [`NsTable`] for a full specification of the binary format of a namespace +//! table. +use crate::{ + full_payload::payload::PayloadByteLen, + namespace_payload::NsPayloadRange, + uint_bytes::{bytes_serde_impl, u32_from_bytes, usize_from_bytes, usize_to_bytes}, + NamespaceId, +}; +use committable::{Commitment, Committable, RawCommitmentBuilder}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::{collections::HashSet, sync::Arc}; + +/// Byte lengths for the different items that could appear in a namespace table. +const NUM_NSS_BYTE_LEN: usize = 4; +const NS_OFFSET_BYTE_LEN: usize = 4; + +// TODO prefer [`NS_ID_BYTE_LEN`] set to `8` because [`NamespaceId`] is a `u64` +// but we need to maintain serialization compatibility. +// https://github.com/EspressoSystems/espresso-sequencer/issues/1574 +const NS_ID_BYTE_LEN: usize = 4; + +/// Raw binary data for a namespace table. +/// +/// Any sequence of bytes is a valid [`NsTable`]. +/// +/// # Binary format of a namespace table +/// +/// Byte lengths for the different items that could appear in a namespace table +/// are specified in local private constants [`NUM_NSS_BYTE_LEN`], +/// [`NS_OFFSET_BYTE_LEN`], [`NS_ID_BYTE_LEN`]. +/// +/// ## Number of entries in the namespace table +/// +/// The first [`NUM_NSS_BYTE_LEN`] bytes of the namespace table indicate the +/// number `n` of entries in the table as a little-endian unsigned integer. If +/// the entire table length is smaller than [`NUM_NSS_BYTE_LEN`] then the +/// missing bytes are zero-padded. +/// +/// The bytes in the namespace table beyond the first [`NUM_NSS_BYTE_LEN`] bytes +/// encode table entries. Each entry consumes exactly [`NS_ID_BYTE_LEN`] `+` +/// [`NS_OFFSET_BYTE_LEN`] bytes. +/// +/// The number `n` could be anything, including a number much larger than the +/// number of entries that could fit in the namespace table. As such, the actual +/// number of entries in the table is defined as the minimum of `n` and the +/// maximum number of whole entries that could fit in the table. +/// +/// See [`Self::in_bounds`] for clarification. +/// +/// ## Namespace table entry +/// +/// ### Namespace ID +/// +/// The first [`NS_ID_BYTE_LEN`] bytes of each table entry indicate the +/// [`NamespaceId`] for this namespace. Any table entry whose [`NamespaceId`] is +/// a duplicate of a previous entry is ignored. A correct count of the number of +/// *unique* (non-ignored) entries is given by `NsTable::iter().count()`. +/// +/// ### Namespace offset +/// +/// The next [`NS_OFFSET_BYTE_LEN`] bytes of each table entry indicate the +/// end-index of a namespace in the block payload bytes +/// [`Payload`](super::payload::Payload). This end-index is a little-endian +/// unsigned integer. +/// +/// # How to deduce a namespace's byte range +/// +/// In order to extract the payload bytes of a single namespace `N` from the +/// block payload one needs both the start- and end-indices for `N`. +/// +/// See [`Self::ns_range`] for clarification. What follows is a description of +/// what's implemented in [`Self::ns_range`]. +/// +/// If `N` occupies the `i`th entry in the namespace table for `i>0` then the +/// start-index for `N` is defined as the end-index of the `(i-1)`th entry in +/// the table. +/// +/// Even if the `(i-1)`the entry would otherwise be ignored (due to a duplicate +/// [`NamespaceId`] or any other reason), that entry's end-index still defines +/// the start-index of `N`. This rule guarantees that both start- and +/// end-indices for any namespace `N` can be read from a constant-size byte +/// range in the namespace table, and it eliminates the need to traverse an +/// unbounded number of previous entries of the namespace table looking for a +/// previous non-ignored entry. +/// +/// The start-index of the 0th entry in the table is implicitly defined to be +/// `0`. +/// +/// The start- and end-indices `(declared_start, declared_end)` declared in the +/// namespace table could be anything. As such, the actual start- and +/// end-indices `(start, end)` are defined so as to ensure that the byte range +/// is well-defined and in-bounds for the block payload: +/// ```ignore +/// end = min(declared_end, block_payload_byte_length) +/// start = min(declared_start, end) +/// ``` +/// +/// In a "honestly-prepared" namespace table the end-index of the final +/// namespace equals the byte length of the block payload. (Otherwise the block +/// payload might have bytes that are not included in any namespace.) +/// +/// It is possible that a namespace table could indicate two distinct namespaces +/// whose byte ranges overlap, though no "honestly-prepared" namespace table +/// would do this. +/// +/// TODO prefer [`NsTable`] to be a newtype like this +/// ```ignore +/// #[repr(transparent)] +/// #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// #[serde(transparent)] +/// pub struct NsTable(#[serde(with = "base64_bytes")] Vec); +/// ``` +/// but we need to maintain serialization compatibility. +/// +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct NsTable { + #[serde(with = "base64_bytes")] + pub bytes: Vec, +} + +impl NsTable { + /// Search the namespace table for the ns_index belonging to `ns_id`. + pub fn find_ns_id(&self, ns_id: &NamespaceId) -> Option { + self.iter() + .find(|index| self.read_ns_id_unchecked(index) == *ns_id) + } + + /// Iterator over all unique namespaces in the namespace table. + pub fn iter(&self) -> impl Iterator + '_ { + NsIter::new(self) + } + + /// Read the namespace id from the `index`th entry from the namespace table. + /// Returns `None` if `index` is out of bounds. + /// + /// TODO I want to restrict visibility to `pub(crate)` or lower but this + /// method is currently used in `nasty-client`. + pub fn read_ns_id(&self, index: &NsIndex) -> Option { + if !self.in_bounds(index) { + None + } else { + Some(self.read_ns_id_unchecked(index)) + } + } + + /// Like [`Self::read_ns_id`] except `index` is not checked. Use [`Self::in_bounds`] as needed. + pub fn read_ns_id_unchecked(&self, index: &NsIndex) -> NamespaceId { + let start = index.0 * (NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN) + NUM_NSS_BYTE_LEN; + + // TODO hack to deserialize `NamespaceId` from `NS_ID_BYTE_LEN` bytes + // https://github.com/EspressoSystems/espresso-sequencer/issues/1574 + NamespaceId::from(u32_from_bytes::( + &self.bytes[start..start + NS_ID_BYTE_LEN], + )) + } + + /// Does the `index`th entry exist in the namespace table? + pub fn in_bounds(&self, index: &NsIndex) -> bool { + // The number of entries in the namespace table, including all duplicate + // namespace IDs. + let num_nss_with_duplicates = std::cmp::min( + // Number of namespaces declared in the ns table + self.read_num_nss(), + // Max number of entries that could fit in the namespace table + self.bytes.len().saturating_sub(NUM_NSS_BYTE_LEN) + / NS_ID_BYTE_LEN.saturating_add(NS_OFFSET_BYTE_LEN), + ); + + index.0 < num_nss_with_duplicates + } + + // CRATE-VISIBLE HELPERS START HERE + + /// Read subslice range for the `index`th namespace from the namespace + /// table. + pub fn ns_range(&self, index: &NsIndex, payload_byte_len: &PayloadByteLen) -> NsPayloadRange { + let end = self.read_ns_offset(index).min(payload_byte_len.as_usize()); + let start = if index.0 == 0 { + 0 + } else { + self.read_ns_offset(&NsIndex(index.0 - 1)) + } + .min(end); + NsPayloadRange::new(start, end) + } + + // PRIVATE HELPERS START HERE + + /// Read the number of namespaces declared in the namespace table. This + /// quantity might exceed the number of entries that could fit in the + /// namespace table. + /// + /// For a correct count of the number of unique namespaces in this + /// namespace table use `iter().count()`. + fn read_num_nss(&self) -> usize { + let num_nss_byte_len = NUM_NSS_BYTE_LEN.min(self.bytes.len()); + usize_from_bytes::(&self.bytes[..num_nss_byte_len]) + } + + /// Read the namespace offset from the `index`th entry from the namespace table. + fn read_ns_offset(&self, index: &NsIndex) -> usize { + let start = + index.0 * (NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN) + NUM_NSS_BYTE_LEN + NS_ID_BYTE_LEN; + usize_from_bytes::(&self.bytes[start..start + NS_OFFSET_BYTE_LEN]) + } + + pub fn encode(&self) -> Arc<[u8]> { + Arc::from(self.bytes.as_ref()) + } +} + +impl Committable for NsTable { + fn commit(&self) -> Commitment { + RawCommitmentBuilder::new(&Self::tag()) + .var_size_bytes(&self.bytes) + .finalize() + } + + fn tag() -> String { + "NSTABLE".into() + } +} + +/// Index for an entry in a ns table. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct NsIndex(usize); +bytes_serde_impl!(NsIndex, to_bytes, [u8; NUM_NSS_BYTE_LEN], from_bytes); + +impl NsIndex { + pub fn to_bytes(&self) -> [u8; NUM_NSS_BYTE_LEN] { + usize_to_bytes::(self.0) + } + fn from_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +/// Return type for [`Payload::ns_iter`]. +pub struct NsIter<'a> { + cur_index: usize, + repeat_nss: HashSet, + ns_table: &'a NsTable, +} + +impl<'a> NsIter<'a> { + pub fn new(ns_table: &'a NsTable) -> Self { + Self { + cur_index: 0, + repeat_nss: HashSet::new(), + ns_table, + } + } +} + +impl<'a> Iterator for NsIter<'a> { + type Item = NsIndex; + + fn next(&mut self) -> Option { + loop { + let candidate_result = NsIndex(self.cur_index); + let ns_id = self.ns_table.read_ns_id(&candidate_result)?; + self.cur_index += 1; + + // skip duplicate namespace IDs + if !self.repeat_nss.insert(ns_id) { + continue; + } + + break Some(candidate_result); + } + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/full_payload/payload.rs b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/payload.rs new file mode 100644 index 0000000000..43080bc60c --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/full_payload/payload.rs @@ -0,0 +1,64 @@ +use crate::full_payload::ns_table::NsTable; +use crate::hotshot_types::{VidCommon, VidSchemeType}; +use jf_vid::VidScheme; +use serde::{Deserialize, Serialize}; +use std::fmt::Display; + +/// Raw payload data for an entire block. +/// +/// A block consists of two sequences of arbitrary bytes: +/// - `ns_table`: namespace table +/// - `ns_payloads`: namespace payloads +/// +/// Any sequence of bytes is a valid `ns_table`. Any sequence of bytes is a +/// valid `ns_payloads`. The contents of `ns_table` determine how to interpret +/// `ns_payload`. +/// +/// # Namespace table +/// +/// See [`NsTable`] for the format of a namespace table. +/// +/// # Namespace payloads +/// +/// A concatenation of payload bytes for multiple individual namespaces. +/// Namespace boundaries are dictated by `ns_table`. See [`NsPayload`] for the +/// format of a namespace payload. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Payload { + // Concatenated payload bytes for each namespace + #[serde(with = "base64_bytes")] + ns_payloads: Vec, + + ns_table: NsTable, +} + +impl Display for Payload { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:#?}") + } +} + +/// Byte length of a block payload, which includes all namespaces but *not* the +/// namespace table. +pub struct PayloadByteLen(usize); + +impl PayloadByteLen { + /// Extract payload byte length from a [`VidCommon`] and construct a new [`Self`] from it. + pub fn from_vid_common(common: &VidCommon) -> Self { + Self(usize::try_from(VidSchemeType::get_payload_byte_len(common)).unwrap()) + } + + /// Is the payload byte length declared in a [`VidCommon`] equal [`Self`]? + pub fn is_consistent(&self, common: &VidCommon) -> Result<(), ()> { + // failure to convert to usize implies that `common` cannot be + // consistent with `self`. + let expected = + usize::try_from(VidSchemeType::get_payload_byte_len(common)).map_err(|_| ())?; + + (self.0 == expected).then_some(()).ok_or(()) + } + + pub fn as_usize(&self) -> usize { + self.0 + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/hotshot_types.rs b/espressocrypto/lib/espresso-crypto-helper/src/hotshot_types.rs new file mode 100644 index 0000000000..5ee3bcfc81 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/hotshot_types.rs @@ -0,0 +1,264 @@ +use committable::{Commitment, Committable, RawCommitmentBuilder}; +use std::ops::Range; +use tagged_base64::tagged; + +use ark_bn254::Bn254; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use digest::OutputSizeUser; +use jf_pcs::{ + prelude::UnivariateUniversalParams, univariate_kzg::UnivariateKzgPCS, + PolynomialCommitmentScheme, +}; +use jf_vid::advz::payload_prover::{LargeRangeProof, SmallRangeProof}; +use jf_vid::{ + advz, + payload_prover::{PayloadProver, Statement}, + precomputable::Precomputable, + VidDisperse, VidResult, VidScheme, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use sha2::Sha256; +use typenum::Unsigned; + +/// Private type alias for the EC pairing type parameter for [`Advz`]. +type E = Bn254; +/// Private type alias for the hash type parameter for [`Advz`]. +type H = Sha256; + +type Advz = advz::Advz; + +pub type VidCommitment = ::Commit; +pub type VidCommon = ::Common; +type Sha256Digest = [u8; ::OutputSize::USIZE]; + +#[tagged("BUILDER_COMMITMENT")] +#[derive(Clone, Debug, Hash, PartialEq, Eq, CanonicalDeserialize, CanonicalSerialize)] +/// Commitment that builders use to sign block options. +/// A thin wrapper around a Sha256 digest. +pub struct BuilderCommitment(Sha256Digest); + +impl AsRef for BuilderCommitment { + fn as_ref(&self) -> &Sha256Digest { + &self.0 + } +} + +/// Type-safe wrapper around `u64` so we know the thing we're talking about is a view number. +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + CanonicalSerialize, + CanonicalDeserialize, +)] +pub struct ViewNumber(pub u64); + +impl Committable for ViewNumber { + fn commit(&self) -> Commitment { + let builder = RawCommitmentBuilder::new("View Number Commitment"); + builder.u64(self.0).finalize() + } +} + +pub struct VidSchemeType(Advz); + +impl VidScheme for VidSchemeType { + type Commit = ::Commit; + type Share = ::Share; + type Common = ::Common; + + fn commit_only(&mut self, payload: B) -> VidResult + where + B: AsRef<[u8]>, + { + self.0.commit_only(payload) + } + + fn disperse(&mut self, payload: B) -> VidResult> + where + B: AsRef<[u8]>, + { + self.0.disperse(payload).map(vid_disperse_conversion) + } + + fn verify_share( + &self, + share: &Self::Share, + common: &Self::Common, + commit: &Self::Commit, + ) -> VidResult> { + self.0.verify_share(share, common, commit) + } + + fn recover_payload(&self, shares: &[Self::Share], common: &Self::Common) -> VidResult> { + self.0.recover_payload(shares, common) + } + + fn is_consistent(commit: &Self::Commit, common: &Self::Common) -> VidResult<()> { + ::is_consistent(commit, common) + } + + fn get_payload_byte_len(common: &Self::Common) -> u32 { + ::get_payload_byte_len(common) + } + + fn get_num_storage_nodes(common: &Self::Common) -> u32 { + ::get_num_storage_nodes(common) + } + + fn get_multiplicity(common: &Self::Common) -> u32 { + ::get_multiplicity(common) + } +} + +impl PayloadProver for VidSchemeType { + fn payload_proof(&self, payload: B, range: Range) -> VidResult + where + B: AsRef<[u8]>, + { + self.0 + .payload_proof(payload, range) + .map(SmallRangeProofType) + } + + fn payload_verify( + &self, + stmt: Statement<'_, Self>, + proof: &SmallRangeProofType, + ) -> VidResult> { + self.0.payload_verify(stmt_conversion(stmt), &proof.0) + } +} + +impl PayloadProver for VidSchemeType { + fn payload_proof(&self, payload: B, range: Range) -> VidResult + where + B: AsRef<[u8]>, + { + self.0 + .payload_proof(payload, range) + .map(LargeRangeProofType) + } + + fn payload_verify( + &self, + stmt: Statement<'_, Self>, + proof: &LargeRangeProofType, + ) -> VidResult> { + self.0.payload_verify(stmt_conversion(stmt), &proof.0) + } +} + +impl Precomputable for VidSchemeType { + type PrecomputeData = ::PrecomputeData; + + fn commit_only_precompute( + &self, + payload: B, + ) -> VidResult<(Self::Commit, Self::PrecomputeData)> + where + B: AsRef<[u8]>, + { + self.0.commit_only_precompute(payload) + } + + fn disperse_precompute( + &self, + payload: B, + data: &Self::PrecomputeData, + ) -> VidResult> + where + B: AsRef<[u8]>, + { + self.0 + .disperse_precompute(payload, data) + .map(vid_disperse_conversion) + } +} + +lazy_static! { + // Initialize the byte array from JSON content + pub static ref KZG_SRS: UnivariateUniversalParams = { + let json_content = include_str!("../vid_srs.json"); + let s: Vec = serde_json::from_str(json_content).expect("Failed to deserialize"); + UnivariateUniversalParams::::deserialize_uncompressed_unchecked(s.as_slice()) + .unwrap() + }; +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct LargeRangeProofType( + // # Type complexity + // + // Jellyfish's `LargeRangeProof` type has a prime field generic parameter `F`. + // This `F` is determined by the type parameter `E` for `Advz`. + // Jellyfish needs a more ergonomic way for downstream users to refer to this type. + // + // There is a `KzgEval` type alias in jellyfish that helps a little, but it's currently private: + // + // If it were public then we could instead use + // `LargeRangeProof>` + // but that's still pretty crufty. + LargeRangeProof< as PolynomialCommitmentScheme>::Evaluation>, +); + +/// Newtype wrapper for a small payload range proof. +/// +/// Useful for transaction proofs. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct SmallRangeProofType( + // # Type complexity + // + // Similar to the comments in `LargeRangeProofType`. + SmallRangeProof< as PolynomialCommitmentScheme>::Proof>, +); + +#[must_use] +pub fn vid_scheme(num_storage_nodes: usize) -> VidSchemeType { + // recovery_threshold is currently num_storage_nodes rounded down to a power of two + // TODO recovery_threshold should be a function of the desired erasure code rate + // https://github.com/EspressoSystems/HotShot/issues/2152 + let recovery_threshold = 1 << num_storage_nodes.ilog2(); + + #[allow(clippy::panic)] + let num_storage_nodes = u32::try_from(num_storage_nodes).unwrap_or_else(|err| { + panic!( + "num_storage_nodes {num_storage_nodes} should fit into u32; \ + error: {err}" + ) + }); + + // TODO panic, return `Result`, or make `new` infallible upstream (eg. by panicking)? + #[allow(clippy::panic)] + VidSchemeType( + Advz::new(num_storage_nodes, recovery_threshold, &*KZG_SRS).unwrap_or_else(|err| { + panic!("advz construction failure: (num_storage nodes,recovery_threshold)=({num_storage_nodes},{recovery_threshold}); \ + error: {err}") + }) + ) +} + +fn stmt_conversion(stmt: Statement<'_, VidSchemeType>) -> Statement<'_, Advz> { + Statement { + payload_subslice: stmt.payload_subslice, + range: stmt.range, + commit: stmt.commit, + common: stmt.common, + } +} + +fn vid_disperse_conversion(vid_disperse: VidDisperse) -> VidDisperse { + VidDisperse { + shares: vid_disperse.shares, + common: vid_disperse.common, + commit: vid_disperse.commit, + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/lib.rs b/espressocrypto/lib/espresso-crypto-helper/src/lib.rs new file mode 100644 index 0000000000..c691e64f1d --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/lib.rs @@ -0,0 +1,188 @@ +mod bytes; +mod full_payload; +mod hotshot_types; +mod namespace_payload; +mod sequencer_data_structures; +mod uint_bytes; +mod utils; +mod v0_3; + +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use committable::{Commitment, Committable}; +use ethers_core::types::U256; +use full_payload::{NsProof, NsTable}; +use hotshot_types::{VidCommitment, VidCommon}; +use jf_crhf::CRHF; +use jf_merkle_tree::prelude::{ + MerkleCommitment, MerkleNode, MerkleProof, MerkleTreeScheme, Sha3Node, +}; +use jf_rescue::{crhf::VariableLengthRescueCRHF, RescueError}; +use sequencer_data_structures::{ + field_to_u256, BlockMerkleCommitment, BlockMerkleTree, Header, Transaction, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use tagged_base64::TaggedBase64; + +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + Default, + CanonicalDeserialize, + CanonicalSerialize, + PartialOrd, + Ord, + Serialize, + Deserialize, +)] +pub struct NamespaceId(u64); + +impl From for u32 { + fn from(value: NamespaceId) -> Self { + value.0 as Self + } +} + +impl From for NamespaceId { + fn from(value: u32) -> Self { + Self(value as u64) + } +} + +// pub type VidScheme = Advz; +pub type Proof = Vec, u64, Sha3Node>>; +pub type CircuitField = ark_ed_on_bn254::Fq; + +// Helper function to verify a block merkle proof. +// proof_bytes: Byte representation of a block merkle proof. +// root_bytes: Byte representation of a Sha3Node merkle root. +// header_bytes: Byte representation of the HotShot header being validated as a Merkle leaf. +// circuit_block_bytes: Circuit representation of the HotShot header commitment returned by the light client contract. +#[no_mangle] +pub extern "C" fn verify_merkle_proof_helper( + proof_ptr: *const u8, + proof_len: usize, + header_ptr: *const u8, + header_len: usize, + block_comm_ptr: *const u8, + block_comm_len: usize, + circuit_block_ptr: *const u8, + circuit_block_len: usize, +) -> bool { + let proof_bytes = unsafe { std::slice::from_raw_parts(proof_ptr, proof_len) }; + let header_bytes = unsafe { std::slice::from_raw_parts(header_ptr, header_len) }; + let block_comm_bytes = unsafe { std::slice::from_raw_parts(block_comm_ptr, block_comm_len) }; + let circuit_block_bytes = + unsafe { std::slice::from_raw_parts(circuit_block_ptr, circuit_block_len) }; + + let proof_str = std::str::from_utf8(proof_bytes).unwrap(); + let header_str = std::str::from_utf8(header_bytes).unwrap(); + let block_comm_str = std::str::from_utf8(block_comm_bytes).unwrap(); + let tagged = TaggedBase64::parse(&block_comm_str).unwrap(); + let block_comm: BlockMerkleCommitment = tagged.try_into().unwrap(); + + let proof: Proof = serde_json::from_str(proof_str).unwrap(); + let header: Header = serde_json::from_str(header_str).unwrap(); + let header_comm: Commitment
= header.commit(); + + let proof = MerkleProof::new(header.height(), proof.to_vec()); + let proved_comm = proof.elem().unwrap().clone(); + BlockMerkleTree::verify(block_comm.digest(), header.height(), proof) + .unwrap() + .unwrap(); + + let mut block_comm_root_bytes = vec![]; + block_comm + .serialize_compressed(&mut block_comm_root_bytes) + .unwrap(); + let field_bytes = hash_bytes_to_field(&block_comm_root_bytes).unwrap(); + let local_block_comm_u256 = field_to_u256(field_bytes); + let circuit_block_comm_u256 = U256::from_little_endian(circuit_block_bytes); + + //assert!(proved_comm == header_comm); + //assert!(local_block_comm_u256 == circuit_block_comm_u256); + + if (proved_comm == header_comm) && (local_block_comm_u256 == circuit_block_comm_u256) { + return true; + } + return false; +} + +// Helper function to verify a VID namespace proof that takes the byte representations of the proof, +// namespace table, and commitment string. +// +// proof_bytes: Byte representation of a JSON NamespaceProof string. +// commit_bytes: Byte representation of a TaggedBase64 payload commitment string. +// ns_table_bytes: Raw bytes of the namespace table. +// tx_comm_bytes: Byte representation of a hex encoded Sha256 digest that the transaction set commits to. +#[no_mangle] +pub extern "C" fn verify_namespace_helper( + namespace: u64, + proof_ptr: *const u8, + proof_len: usize, + commit_ptr: *const u8, + commit_len: usize, + ns_table_ptr: *const u8, + ns_table_len: usize, + tx_comm_ptr: *const u8, + tx_comm_len: usize, + common_data_ptr: *const u8, + common_data_len: usize, +) -> bool { + let ns_table_bytes = unsafe { std::slice::from_raw_parts(ns_table_ptr, ns_table_len) }; + let proof_bytes = unsafe { std::slice::from_raw_parts(proof_ptr, proof_len) }; + let commit_bytes = unsafe { std::slice::from_raw_parts(commit_ptr, commit_len) }; + let tx_comm_bytes = unsafe { std::slice::from_raw_parts(tx_comm_ptr, tx_comm_len) }; + let common_data_bytes = unsafe { std::slice::from_raw_parts(common_data_ptr, common_data_len) }; + + let proof_str = std::str::from_utf8(proof_bytes).unwrap(); + let commit_str = std::str::from_utf8(commit_bytes).unwrap(); + let txn_comm_str = std::str::from_utf8(tx_comm_bytes).unwrap(); + let common_data_str = std::str::from_utf8(common_data_bytes).unwrap(); + + let proof: NsProof = serde_json::from_str(proof_str).unwrap(); + let ns_table: NsTable = NsTable { + bytes: ns_table_bytes.to_vec(), + }; + let tagged = TaggedBase64::parse(&commit_str).unwrap(); + let commit: VidCommitment = tagged.try_into().unwrap(); + let vid_common: VidCommon = serde_json::from_str(common_data_str).unwrap(); + + let (txns, ns) = proof.verify(&ns_table, &commit, &vid_common).unwrap(); + + let namespace: u32 = namespace.try_into().unwrap(); + let txns_comm = hash_txns(namespace, &txns); + + //assert!(ns == namespace.into()); + //assert!(txns_comm == txn_comm_str); + if (ns == namespace.into()) && (txns_comm == txn_comm_str) { + return true; + } + return false; +} + +// TODO: Use Commit trait: https://github.com/EspressoSystems/nitro-espresso-integration/issues/88 +fn hash_txns(namespace: u32, txns: &[Transaction]) -> String { + let mut hasher = Sha256::new(); + hasher.update(namespace.to_le_bytes()); + for txn in txns { + hasher.update(&txn.payload); + } + let hash_result = hasher.finalize(); + format!("{:x}", hash_result) +} + +fn hash_bytes_to_field(bytes: &[u8]) -> Result { + // make sure that `mod_order` won't happen. + let bytes_len = ((::MODULUS_BIT_SIZE + 7) / 8 - 1) as usize; + let elem = bytes + .chunks(bytes_len) + .map(CircuitField::from_le_bytes_mod_order) + .collect::>(); + Ok(VariableLengthRescueCRHF::<_, 1>::evaluate(elem)?[0]) +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/mock_data/header.json b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/header.json new file mode 100644 index 0000000000..78ffc95fbf --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/header.json @@ -0,0 +1,37 @@ +{ + "block_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQA", + "builder_commitment": "BUILDER_COMMITMENT~PfISPEAHYbCXqJ08RqlK74d9aSrtrZkKHdnoX7crymG5", + "builder_signature": { + "r": "0xca39647d5e159ebf62e0efd163ddb2b7946f437948e6c31accdb5360a9f2da17", + "s": "0x610e51dced89ede20e3dcc5e9e26fff580738e785371df53389f2f84dadbdb87", + "v": 28 + }, + "chain_config": { + "chain_config": { + "Left": { + "base_fee": "0", + "chain_id": "35353", + "fee_contract": "0x0000000000000000000000000000000000000000", + "fee_recipient": "0x0000000000000000000000000000000000000000", + "max_block_size": "10240" + } + } + }, + "fee_info": { + "account": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "amount": "0" + }, + "fee_merkle_tree_root": "MERKLE_COMM~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAKA", + "height": 42, + "l1_finalized": { + "hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "number": 123, + "timestamp": "0x456" + }, + "l1_head": 124, + "ns_table": { + "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "payload_commitment": "HASH~AazstQer_ho1SqgGT0r10_Gs0BnjfbPBHJdSO3HHbp29", + "timestamp": 789 +} \ No newline at end of file diff --git a/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_header.json b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_header.json new file mode 100644 index 0000000000..6def47976b --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_header.json @@ -0,0 +1,31 @@ +{ + "chain_config": { + "chain_config": { + "Right": "CHAIN_CONFIG~E_LT4O8oRs3FoSL7pVT1iVaN9Y2XAHNYnKmJuHcz2mXE" + } + }, + "height": 1, + "timestamp": 1714012810, + "l1_head": 40, + "l1_finalized": { + "number": 32, + "timestamp": "0x6629c282", + "hash": "0x086c2d889dfc549acffa6ecd53b4df088e770ac87e26e3726d09f849c17db536" + }, + "payload_commitment": "HASH~CcLCyX4_jMHMT_UXK_FsGF8Shvw7pgFj5Og2mjW2y7ao", + "builder_commitment": "BUILDER_COMMITMENT~qfiL_spBvy7D19-v1bjtjhB_Yg1oH84s5xE1A5YgMn42", + "ns_table": { + "bytes": "CgAAABgnAACUCAAAGScAAG4TAAASJwAAcx4AABQnAADQIgAAECcAAOAmAAAWJwAARy8AABMnAABTLwAAEScAALsyAAAXJwAATjoAABonAAAJRQAA" + }, + "block_merkle_tree_root": "MERKLE_COMM~Y9ufYrqN9PnWxV7sGibwrfdNKXkgGzmL2TZDrLZgo1AgAAAAAAAAAAEAAAAAAAAAJA", + "fee_merkle_tree_root": "MERKLE_COMM~bYMmugVZtniEfwFcBGTEbMpBnkPHHWmt8YAW5ygWSKAUAAAAAAAAAAYAAAAAAAAAvQ", + "builder_signature": { + "r": "0x95824a15bb9e854bfbbab2b4ea27c82102078691281b1dc24fb5d63bb2deb663", + "s": "0x4419db237be39bfc44cb37f2f69ed78cd1709fe4b01ec15c0bbd08ddd309d7b8", + "v": 27 + }, + "fee_info": { + "account": "0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097", + "amount": "0" + } +} \ No newline at end of file diff --git a/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_merkle_path.json b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_merkle_path.json new file mode 100644 index 0000000000..5e2bf3a4c2 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/mock_data/test_merkle_path.json @@ -0,0 +1,461 @@ +[ + { + "Leaf": { + "value": "FIELD~6VQrizdRaPzfmROuCvnc0QOwBdhcwKY_3EPHynjDyILQ", + "pos": "FIELD~AQAAAAAAAADo", + "elem": "FIELD~RSru5Twf7bIO0OjFSgP51vzmoK0F-8hRzT2PupCj0tMI" + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~xscaz6FruKxsBoKEACvNF1wnTW6V0rH0j29ig-bAbUwd" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~6VQrizdRaPzfmROuCvnc0QOwBdhcwKY_3EPHynjDyILQ" + } + }, + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~pQJi6QyIJsBp2QjXewKEnmX69AlAUDcJn91qDQOBu0d5" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~xVk0KVx9gGlqJezxTFWJb5VY0W5HR0fGOpU4jA7jCZZK" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~gl-2rVZixvayTCL9oF_JqaGLaa_wE_mbFOqFZjqPCzZA" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~xL0xIv0h68mYL11kWao6rYmSTEWi3fRbyQWHfPyEn_iE" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~h-cXbgHjUsA5UdjCKElfotbH_sFojDsr-Yi1OCZfJqt6" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~5szgmqzQUFqVqv-KMC23Lge1V4VX283P7AL4TzccF7Nj" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~sFf1-LCN8CKAqF1_qiJtg_2C8vUQ4Hsqc234C4114Fcv" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~oSrlsy2v9cJ-JSq2S5C-vFmLLhU65TwQ3NCyfbYdtqT6" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~0peohqKgYojAT6KNF1bBsRNmlygaKQzLaw07j6Z2PMRv" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~z9CXaMo00duLJpu-oy9wRt3E8CO1pous0G8cF9RaK9H7" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~jta1zsZ3wwspHq6kPdlLJnUuq6OZLQXfpVcTBFIwrP33" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~M_1B3DZvnKF-pCIWkA1EPaAJ9fbKvXATbuVKfFaViMUR" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~FvTrLXCDsQsXcSaw0dXXI6IGZvgERzlICbYOrIg9HDc5" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~i5GrTe_LAyQ-l9xC1-Z8PNRVGLQlvtlOXkDPXsC8uPzS" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~hflM-xcou4mbpNssmyi0psXcmLsdKHYbNIsOUwfq-kX9" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~k8zu7RN20jkn1HZhC8rZs4Blg4m4ot2UHYOKPFqVsrmv" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~aVOpFFV-GDvlEMuccVQs58g5OLC1Yh8m6mjRwRVWKZ-i" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~qsrtaYOIFWg45Xm9Gjy8a9OdjIn4lKv3RSN3AtxBsik0" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~7xmzCbMCAbgamlgx05_WjSlxB477ITa-pZb2bL2raPYH" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~SOiYDaqzZK2_F6Gf9lTzhVG_4StGIRSPIBiMFhe-RQG1" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~TSDISEgvVqX6tmEkvDtvH-fYVIPdA7XuQ2MwotMdzArQ" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~lMFy8dCVMzxI1ZW1mzrHRlTw3tMwAbGKROTYwEMLwSkA" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~pgXvTrhjfZeoviD2eaU2V2Fw_ChA2wX8kcQo0957y389" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~FjM7j4P8ceGWUnHI6Yj6HBfJKund6Ydpp9cjoC3j1a8P" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~YK-KU5izKQQesnWJ-e_0BDqlHQRcU_SF7GHF7W0KRK01" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~TClCNKKoFWIqeb0vgib8Ec0b6rZuYspnsfpuss8qRgqA" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~d2r9j_P8ej-NZioHkgplpNr2e8cIWqUUvdTHV5LSimuL" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~7f4ZkitmDas3uutCpjF7KBKWIugwSM9lCfw0UOaIV2er" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~Atx2Q-bXGa4dUQ2z3vf5TDixMjTyHelLEwdCoNu-cXjr" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~YJRraa2xh7phTgoIDWDxN5UVIYRpUlQy_FhhCHrsPKqX" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~Y-tbmH2qO7bCByGa0XidyQJKeC1-ZYrtnn_AIPEzTnBW" + } + }, + "Empty", + "Empty" + ] + } + } +] \ No newline at end of file diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload.rs new file mode 100644 index 0000000000..222cabc3ff --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload.rs @@ -0,0 +1,8 @@ +mod iter; +mod ns_payload; +mod ns_payload_range; +mod tx_proof; +mod types; + +pub use ns_payload::NsPayloadOwned; +pub use ns_payload_range::NsPayloadRange; diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/iter.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/iter.rs new file mode 100644 index 0000000000..572e2c518d --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/iter.rs @@ -0,0 +1,8 @@ +use crate::{full_payload::NsIndex, namespace_payload::types::TxIndex}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Index { + ns_index: NsIndex, + tx_index: TxIndex, +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload.rs new file mode 100644 index 0000000000..77dd322a4e --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload.rs @@ -0,0 +1,116 @@ +use crate::{ + namespace_payload::types::{ + FromNsPayloadBytes, NsPayloadByteLen, NsPayloadBytesRange, NumTxs, NumTxsRange, + NumTxsUnchecked, TxIndex, TxIter, TxPayloadRange, TxTableEntriesRange, + }, + NamespaceId, Transaction, +}; +use serde::{Deserialize, Serialize}; + +/// Raw binary data for a single namespace's payload. +/// +/// Any sequence of bytes is a valid [`NsPayload`]. +/// +/// See module-level documentation [`types`](super::types) for a full +/// specification of the binary format of a namespace. +pub struct NsPayload([u8]); + +impl NsPayload { + pub fn as_bytes_slice(&self) -> &[u8] { + &self.0 + } + pub fn byte_len(&self) -> NsPayloadByteLen { + NsPayloadByteLen::from_usize(self.0.len()) + } + + /// Read and parse bytes from the ns payload. + /// + /// Arg `range: &R` is convertible into a `Range` via + /// [`NsPayloadBytesRange`]. The payload bytes are parsed into a `R::Output` + /// via [`FromNsPayloadBytes`]. + pub fn read<'a, R>(&'a self, range: &R) -> R::Output + where + R: NsPayloadBytesRange<'a>, + { + >::from_payload_bytes(&self.0[range.ns_payload_range()]) + } + + /// Return all transactions in this namespace. The namespace ID for each + /// returned [`Transaction`] is set to `ns_id`. + pub fn export_all_txs(&self, ns_id: &NamespaceId) -> Vec { + let num_txs = self.read_num_txs(); + self.iter_from_num_txs(&num_txs) + .map(|i| self.tx_from_num_txs(ns_id, &i, &num_txs)) + .collect() + } + + /// Private helper. (Could be pub if desired.) + fn read_num_txs(&self) -> NumTxsUnchecked { + self.read(&NumTxsRange::new(&self.byte_len())) + } + + /// Private helper + fn iter_from_num_txs(&self, num_txs: &NumTxsUnchecked) -> TxIter { + let num_txs = NumTxs::new(num_txs, &self.byte_len()); + TxIter::new(&num_txs) + } + + /// Private helper + fn tx_from_num_txs( + &self, + ns_id: &NamespaceId, + index: &TxIndex, + num_txs_unchecked: &NumTxsUnchecked, + ) -> Transaction { + let tx_table_entries = self.read(&TxTableEntriesRange::new(index)); + let tx_range = TxPayloadRange::new(num_txs_unchecked, &tx_table_entries, &self.byte_len()); + + // TODO don't copy the tx bytes into the return value + // https://github.com/EspressoSystems/hotshot-query-service/issues/267 + let tx_payload = self.read(&tx_range).to_payload_bytes().to_vec(); + Transaction::new(*ns_id, tx_payload) + } +} + +#[repr(transparent)] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(transparent)] +pub struct NsPayloadOwned(#[serde(with = "base64_bytes")] Vec); + +/// Crazy boilerplate code to make it so that [`NsPayloadOwned`] is to +/// [`NsPayload`] as [`Vec`] is to `[T]`. See [How can I create newtypes for +/// an unsized type and its owned counterpart (like `str` and `String`) in safe +/// Rust? - Stack Overflow](https://stackoverflow.com/q/64977525) +mod ns_payload_owned { + use super::{NsPayload, NsPayloadOwned}; + use std::borrow::Borrow; + use std::ops::Deref; + + impl NsPayload { + // pub(super) because I want it visible everywhere in this file but I + // also want this boilerplate code quarrantined in `ns_payload_owned`. + pub(super) fn new_private(p: &[u8]) -> &NsPayload { + unsafe { &*(p as *const [u8] as *const NsPayload) } + } + } + + impl Deref for NsPayloadOwned { + type Target = NsPayload; + fn deref(&self) -> &NsPayload { + NsPayload::new_private(&self.0) + } + } + + impl Borrow for NsPayloadOwned { + fn borrow(&self) -> &NsPayload { + self.deref() + } + } + + impl ToOwned for NsPayload { + type Owned = NsPayloadOwned; + fn to_owned(&self) -> NsPayloadOwned { + NsPayloadOwned(self.0.to_owned()) + } + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload_range.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload_range.rs new file mode 100644 index 0000000000..3846223839 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/ns_payload_range.rs @@ -0,0 +1,34 @@ +use super::types::{NsPayloadByteLen, NsPayloadBytesRange}; +use std::ops::Range; + +/// Index range for a namespace payload inside a block payload. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct NsPayloadRange(Range); + +impl NsPayloadRange { + /// TODO restrict visibility? + pub fn new(start: usize, end: usize) -> Self { + Self(start..end) + } + + /// Access the underlying index range for this namespace inside a block + /// payload. + pub fn as_block_range(&self) -> Range { + self.0.clone() + } + + /// Return the byte length of this namespace. + pub fn byte_len(&self) -> NsPayloadByteLen { + NsPayloadByteLen::from_usize(self.0.len()) + } + + /// Convert a [`NsPayloadBytesRange`] into a range that's relative to the + /// entire block payload. + pub fn block_range<'a, R>(&self, range: &R) -> Range + where + R: NsPayloadBytesRange<'a>, + { + let range = range.ns_payload_range(); + range.start + self.0.start..range.end + self.0.start + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/tx_proof.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/tx_proof.rs new file mode 100644 index 0000000000..5c066f8e21 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/tx_proof.rs @@ -0,0 +1,24 @@ +use crate::hotshot_types::SmallRangeProofType; +use crate::namespace_payload::types::{NumTxsUnchecked, TxIndex, TxTableEntries}; +use serde::{Deserialize, Serialize}; + +/// Proof of correctness for transaction bytes in a block. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct TxProof { + // Naming conventions for this struct's fields: + // - `payload_x`: bytes from the payload + // - `payload_proof_x`: a proof of those bytes from the payload + tx_index: TxIndex, + + // Number of txs declared in the tx table + payload_num_txs: NumTxsUnchecked, + payload_proof_num_txs: SmallRangeProofType, + + // Tx table entries for this tx + payload_tx_table_entries: TxTableEntries, + payload_proof_tx_table_entries: SmallRangeProofType, + + // This tx's payload bytes. + // `None` if this tx has zero length. + payload_proof_tx: Option, +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/types.rs b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/types.rs new file mode 100644 index 0000000000..8c21e02759 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/namespace_payload/types.rs @@ -0,0 +1,382 @@ +//! Types related to a namespace payload and its transaction table. +//! +//! All code that needs to know the binary format of a namespace payload and its +//! transaction table is restricted to this file. +//! +//! There are many newtypes in this file to facilitate transaction proofs. +//! +//! # Binary format of a namespace payload +//! +//! Any sequence of bytes is a valid [`NsPayload`]. +//! +//! A namespace payload consists of two concatenated byte sequences: +//! - `tx_table`: transaction table +//! - `tx_payloads`: transaction payloads +//! +//! # Transaction table +//! +//! Byte lengths for the different items that could appear in a `tx_table` are +//! specified in local private constants [`NUM_TXS_BYTE_LEN`], +//! [`TX_OFFSET_BYTE_LEN`]. +//! +//! ## Number of entries in the transaction table +//! +//! The first [`NUM_TXS_BYTE_LEN`] bytes of the `tx_table` indicate the number +//! `n` of entries in the table as a little-endian unsigned integer. If the +//! entire namespace payload byte length is smaller than [`NUM_TXS_BYTE_LEN`] +//! then the missing bytes are zero-padded. +//! +//! The bytes in the namespace payload beyond the first [`NUM_TXS_BYTE_LEN`] +//! bytes encode entries in the `tx_table`. Each entry consumes exactly +//! [`TX_OFFSET_BYTE_LEN`] bytes. +//! +//! The number `n` could be anything, including a number much larger than the +//! number of entries that could fit in the namespace payload. As such, the +//! actual number of entries in the `tx_table` is defined as the minimum of `n` +//! and the maximum number of whole `tx_table` entries that could fit in the +//! namespace payload. +//! +//! The `tx_payloads` consist of any bytes in the namespace payload beyond the +//! `tx_table`. +//! +//! ## Transaction table entry +//! +//! Each entry in the `tx_table` is exactly [`TX_OFFSET_BYTE_LEN`] bytes. These +//! bytes indicate the end-index of a transaction in the namespace payload +//! bytes. This end-index is a little-endian unsigned integer. +//! +//! This offset is relative to the end of the `tx_table` within the current +//! namespace. +//! +//! ### Example +//! +//! Suppose a block payload has 3000 bytes and 3 namespaces of 1000 bytes each. +//! Suppose the `tx_table` for final namespace in the block has byte length 100, +//! and suppose an entry in that `tx_table` indicates an end-index of `10`. The +//! actual end-index of that transaction relative to the current namespace is +//! `110`: `10` bytes for the offset plus `100` bytes for the `tx_table`. +//! Relative to the entire block payload, the end-index of that transaction is +//! `2110`: `10` bytes for the offset plus `100` bytes for the `tx_table` plus +//! `2000` bytes for this namespace. +//! +//! # How to deduce a transaction's byte range +//! +//! In order to extract the payload bytes of a single transaction `T` from the +//! namespace payload one needs both the start- and end-indices for `T`. +//! +//! See [`TxPayloadRange::new`] for clarification. What follows is a description +//! of what's implemented in [`TxPayloadRange::new`]. +//! +//! If `T` occupies the `i`th entry in the `tx_table` for `i>0` then the +//! start-index for `T` is defined as the end-index of the `(i-1)`th entry in +//! the table. +//! +//! Thus, both start- and end-indices for any transaction `T` can be read from a +//! contiguous, constant-size byte range in the `tx_table`. This property +//! facilitates transaction proofs. +//! +//! The start-index of the 0th entry in the table is implicitly defined to be +//! `0`. +//! +//! The start- and end-indices `(declared_start, declared_end)` declared in the +//! `tx_table` could be anything. As such, the actual start- and end-indices +//! `(start, end)` are defined so as to ensure that the byte range is +//! well-defined and in-bounds for the namespace payload: +//! ```ignore +//! end = min(declared_end, namespace_payload_byte_length) +//! start = min(declared_start, end) +//! ``` +//! +//! To get the byte range for `T` relative to the current namespace, the above +//! range is translated by the byte length of the `tx_table` *as declared in the +//! `tx_table` itself*, suitably truncated to fit within the current namespace. +//! +//! In particular, if the `tx_table` declares a huge number `n` of entries that +//! cannot fit into the namespace payload then all transactions in this +//! namespace have a zero-length byte range whose start- and end-indices are +//! both `namespace_payload_byte_length`. +//! +//! In a "honestly-prepared" `tx_table` the end-index of the final transaction +//! equals the byte length of the namespace payload minus the byte length of the +//! `tx_table`. (Otherwise the namespace payload might have bytes that are not +//! included in any transaction.) +//! +//! It is possible that a `tx_table` table could indicate two distinct +//! transactions whose byte ranges overlap, though no "honestly-prepared" +//! `tx_table` would do this. +use crate::uint_bytes::{bytes_serde_impl, usize_from_bytes, usize_to_bytes}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::ops::Range; + +/// Byte lengths for the different items that could appear in a tx table. +const NUM_TXS_BYTE_LEN: usize = 4; +const TX_OFFSET_BYTE_LEN: usize = 4; + +/// Data that can be deserialized from a subslice of namespace payload bytes. +/// +/// Companion trait for [`NsPayloadBytesRange`], which specifies the subslice of +/// namespace payload bytes to read. +pub trait FromNsPayloadBytes<'a> { + /// Deserialize `Self` from namespace payload bytes. + fn from_payload_bytes(bytes: &'a [u8]) -> Self; +} + +/// Specifies a subslice of namespace payload bytes to read. +/// +/// Companion trait for [`FromNsPayloadBytes`], which holds data that can be +/// deserialized from that subslice of bytes. +pub trait NsPayloadBytesRange<'a> { + type Output: FromNsPayloadBytes<'a>; + + /// Range relative to this ns payload + fn ns_payload_range(&self) -> Range; +} + +/// Number of txs in a namespace. +/// +/// Like [`NumTxsUnchecked`] but checked against a [`NsPayloadByteLen`]. +pub struct NumTxs(usize); + +impl NumTxs { + /// Returns the minimum of: + /// - `num_txs` + /// - The maximum number of tx table entries that could fit in a namespace + /// whose byte length is `byte_len`. + pub fn new(num_txs: &NumTxsUnchecked, byte_len: &NsPayloadByteLen) -> Self { + Self(std::cmp::min( + // Number of txs declared in the tx table + num_txs.0, + // Max number of tx table entries that could fit in the namespace payload + byte_len.0.saturating_sub(NUM_TXS_BYTE_LEN) / TX_OFFSET_BYTE_LEN, + )) + } +} + +/// Byte length of a namespace payload. +pub struct NsPayloadByteLen(usize); + +impl NsPayloadByteLen { + // TODO restrict visibility? + pub fn from_usize(n: usize) -> Self { + Self(n) + } +} + +/// The part of a tx table that declares the number of txs in the payload. +/// +/// "Unchecked" because this quantity might exceed the number of tx table +/// entries that could fit into the namespace that contains it. +/// +/// Use [`NumTxs`] for the actual number of txs in this namespace. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct NumTxsUnchecked(usize); +bytes_serde_impl!( + NumTxsUnchecked, + to_payload_bytes, + [u8; NUM_TXS_BYTE_LEN], + from_payload_bytes +); + +impl NumTxsUnchecked { + pub fn to_payload_bytes(&self) -> [u8; NUM_TXS_BYTE_LEN] { + usize_to_bytes::(self.0) + } +} + +impl FromNsPayloadBytes<'_> for NumTxsUnchecked { + fn from_payload_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +/// Byte range for the part of a tx table that declares the number of txs in the +/// payload. +pub struct NumTxsRange(Range); + +impl NumTxsRange { + pub fn new(byte_len: &NsPayloadByteLen) -> Self { + Self(0..NUM_TXS_BYTE_LEN.min(byte_len.0)) + } +} + +impl NsPayloadBytesRange<'_> for NumTxsRange { + type Output = NumTxsUnchecked; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// Entries from a tx table in a namespace for use in a transaction proof. +/// +/// Contains either one or two entries according to whether it was derived from +/// the first transaction in the namespace. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TxTableEntries { + cur: usize, + prev: Option, // `None` if derived from the first transaction +} + +// This serde impl uses Vec. We could save space by using an array of +// length `TWO_ENTRIES_BYTE_LEN`, but then we need a way to distinguish +// `prev=Some(0)` from `prev=None`. +bytes_serde_impl!( + TxTableEntries, + to_payload_bytes, + Vec, + from_payload_bytes +); + +impl TxTableEntries { + const TWO_ENTRIES_BYTE_LEN: usize = 2 * TX_OFFSET_BYTE_LEN; + + pub fn to_payload_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(Self::TWO_ENTRIES_BYTE_LEN); + if let Some(prev) = self.prev { + bytes.extend(usize_to_bytes::(prev)); + } + bytes.extend(usize_to_bytes::(self.cur)); + bytes + } +} + +impl FromNsPayloadBytes<'_> for TxTableEntries { + fn from_payload_bytes(bytes: &[u8]) -> Self { + match bytes.len() { + TX_OFFSET_BYTE_LEN => Self { + cur: usize_from_bytes::(bytes), + prev: None, + }, + Self::TWO_ENTRIES_BYTE_LEN => Self { + cur: usize_from_bytes::(&bytes[TX_OFFSET_BYTE_LEN..]), + prev: Some(usize_from_bytes::( + &bytes[..TX_OFFSET_BYTE_LEN], + )), + }, + len => panic!( + "unexpected bytes len {} should be either {} or {}", + len, + TX_OFFSET_BYTE_LEN, + Self::TWO_ENTRIES_BYTE_LEN + ), + } + } +} + +/// Byte range for entries from a tx table for use in a transaction proof. +/// +/// This range covers either one or two entries from a tx table according to +/// whether it was derived from the first transaction in the namespace. +pub struct TxTableEntriesRange(Range); + +impl TxTableEntriesRange { + pub fn new(index: &TxIndex) -> Self { + let start = if index.0 == 0 { + // Special case: the desired range includes only one entry from + // the tx table: the first entry. This entry starts immediately + // following the bytes that encode the tx table length. + NUM_TXS_BYTE_LEN + } else { + // The desired range starts at the beginning of the previous tx + // table entry. + (index.0 - 1) + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN) + }; + // The desired range ends at the end of this transaction's tx table entry + let end = index + .0 + .saturating_add(1) + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN); + Self(start..end) + } +} + +impl NsPayloadBytesRange<'_> for TxTableEntriesRange { + type Output = TxTableEntries; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// A transaction's payload data. +pub struct TxPayload<'a>(&'a [u8]); + +impl<'a> TxPayload<'a> { + pub fn to_payload_bytes(&self) -> &'a [u8] { + self.0 + } +} + +impl<'a> FromNsPayloadBytes<'a> for TxPayload<'a> { + fn from_payload_bytes(bytes: &'a [u8]) -> Self { + Self(bytes) + } +} + +/// Byte range for a transaction's payload data. +pub struct TxPayloadRange(Range); + +impl TxPayloadRange { + pub fn new( + num_txs: &NumTxsUnchecked, + tx_table_entries: &TxTableEntries, + byte_len: &NsPayloadByteLen, + ) -> Self { + let tx_table_byte_len = num_txs + .0 + .saturating_mul(TX_OFFSET_BYTE_LEN) + .saturating_add(NUM_TXS_BYTE_LEN); + let end = tx_table_entries + .cur + .saturating_add(tx_table_byte_len) + .min(byte_len.0); + let start = tx_table_entries + .prev + .unwrap_or(0) + .saturating_add(tx_table_byte_len) + .min(end); + Self(start..end) + } +} + +impl<'a> NsPayloadBytesRange<'a> for TxPayloadRange { + type Output = TxPayload<'a>; + + fn ns_payload_range(&self) -> Range { + self.0.clone() + } +} + +/// Index for an entry in a tx table. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct TxIndex(usize); +bytes_serde_impl!(TxIndex, to_bytes, [u8; NUM_TXS_BYTE_LEN], from_bytes); + +impl TxIndex { + pub fn to_bytes(&self) -> [u8; NUM_TXS_BYTE_LEN] { + usize_to_bytes::(self.0) + } + fn from_bytes(bytes: &[u8]) -> Self { + Self(usize_from_bytes::(bytes)) + } +} + +pub struct TxIter(Range); + +impl TxIter { + pub fn new(num_txs: &NumTxs) -> Self { + Self(0..num_txs.0) + } +} + +// Simple `impl Iterator` delegates to `Range`. +impl Iterator for TxIter { + type Item = TxIndex; + + fn next(&mut self) -> Option { + self.0.next().map(TxIndex) + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/sequencer_data_structures.rs b/espressocrypto/lib/espresso-crypto-helper/src/sequencer_data_structures.rs new file mode 100644 index 0000000000..19ed706eb5 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/sequencer_data_structures.rs @@ -0,0 +1,797 @@ +// TODO import from sequencer: https://github.com/EspressoSystems/nitro-espresso-integration/issues/87 +// This module is essentially copy and pasted VID logic from the sequencer repo. It is an unfortunate workaround +// until the VID portion of the sequencer repo is WASM-compatible. +use ark_ff::{BigInteger, PrimeField}; +use ark_serialize::{ + CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate, +}; +use bytesize::ByteSize; +use committable::{Commitment, Committable, RawCommitmentBuilder}; +use derive_more::Deref; +use derive_more::{Add, Display, From, Into, Sub}; +use digest::OutputSizeUser; +use either::Either; +use ethers_core::{ + types::{Address, Signature, H256, U256}, + utils::{parse_units, ParseUnits}, +}; +use jf_merkle_tree::{ + prelude::{LightWeightSHA3MerkleTree, Sha3Digest, Sha3Node}, + universal_merkle_tree::UniversalMerkleTree, + MerkleTreeScheme, ToTraversalPath, +}; +use num_traits::PrimInt; +use serde::Serializer; +use serde::{ + de::{self, MapAccess, SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, +}; +use serde_json::{Map, Value}; +use std::{default::Default, fmt, str::FromStr}; +use tagged_base64::tagged; +use trait_set::trait_set; +use typenum::Unsigned; + +use crate::v0_3; +use crate::{full_payload::NsTable, hotshot_types::VidCommitment}; +use crate::{ + utils::{impl_serde_from_string_or_integer, Err, FromStringOrInteger}, + NamespaceId, +}; + +trait_set! { + pub trait TableWordTraits = CanonicalSerialize + + CanonicalDeserialize + + TryFrom + + TryInto + + Default + + PrimInt + + std::marker::Sync; + + // Note: this trait is not used yet as for now the Payload structs are only parametrized with the TableWord parameter. + pub trait OffsetTraits = CanonicalSerialize + + CanonicalDeserialize + + TryFrom + + TryInto + + Default + + std::marker::Sync; + + // Note: this trait is not used yet as for now the Payload structs are only parametrized with the TableWord parameter. + pub trait NsIdTraits =CanonicalSerialize + CanonicalDeserialize + Default + std::marker::Sync; +} + +pub struct Transaction { + pub _namespace: NamespaceId, + pub payload: Vec, +} + +impl Transaction { + pub fn new(namespace: NamespaceId, payload: Vec) -> Self { + Self { + _namespace: namespace, + payload, + } + } +} + +#[derive(Default, Hash, Copy, Clone, Debug, PartialEq, Eq, From, Into, Display)] +#[display(fmt = "{_0}")] + +pub struct ChainId(U256); + +impl From for ChainId { + fn from(id: u64) -> Self { + Self(id.into()) + } +} + +impl FromStringOrInteger for ChainId { + type Binary = U256; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> Result { + Ok(i.into()) + } + + fn from_string(s: String) -> Result { + if s.starts_with("0x") { + Ok(Self(U256::from_str(&s).unwrap())) + } else { + Ok(Self(U256::from_dec_str(&s).unwrap())) + } + } + + fn to_binary(&self) -> Result { + Ok(self.0) + } + + fn to_string(&self) -> Result { + Ok(format!("{self}")) + } +} + +macro_rules! impl_to_fixed_bytes { + ($struct_name:ident, $type:ty) => { + impl $struct_name { + pub(crate) fn to_fixed_bytes(self) -> [u8; core::mem::size_of::<$type>()] { + let mut bytes = [0u8; core::mem::size_of::<$type>()]; + self.0.to_little_endian(&mut bytes); + bytes + } + } + }; +} + +impl_serde_from_string_or_integer!(ChainId); +impl_to_fixed_bytes!(ChainId, U256); + +impl From for ChainId { + fn from(id: u16) -> Self { + Self(id.into()) + } +} + +#[derive(Hash, Copy, Clone, Debug, Default, Display, PartialEq, Eq, From, Into, Deref)] +#[display(fmt = "{_0}")] +pub struct BlockSize(u64); + +impl_serde_from_string_or_integer!(BlockSize); + +impl FromStringOrInteger for BlockSize { + type Binary = u64; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> Result { + Ok(Self(i)) + } + + fn from_string(s: String) -> Result { + Ok(BlockSize(s.parse::().unwrap().0)) + } + + fn to_binary(&self) -> Result { + Ok(self.0) + } + + fn to_string(&self) -> Result { + Ok(format!("{self}")) + } +} + +/// Global variables for an Espresso blockchain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ChainConfig { + /// Espresso chain ID + chain_id: ChainId, + /// Maximum size in bytes of a block + max_block_size: BlockSize, + /// Minimum fee in WEI per byte of payload + base_fee: FeeAmount, + fee_contract: Option
, + fee_recipient: FeeAccount, +} + +impl Committable for ChainConfig { + fn tag() -> String { + "CHAIN_CONFIG".to_string() + } + + fn commit(&self) -> Commitment { + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) + .fixed_size_field("chain_id", &self.chain_id.to_fixed_bytes()) + .u64_field("max_block_size", self.max_block_size.0) + .fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes()) + .fixed_size_field("fee_recipient", &self.fee_recipient.to_fixed_bytes()); + let comm = if let Some(addr) = self.fee_contract { + comm.u64_field("fee_contract", 1).fixed_size_bytes(&addr.0) + } else { + comm.u64_field("fee_contract", 0) + }; + comm.finalize() + } +} + +#[derive(Clone, Debug, Copy, PartialEq, Deserialize, Serialize, Eq, Hash)] +pub struct ResolvableChainConfig { + chain_config: Either>, +} + +impl ResolvableChainConfig { + pub fn commit(&self) -> Commitment { + match self.chain_config { + Either::Left(config) => config.commit(), + Either::Right(commitment) => commitment, + } + } + pub fn resolve(self) -> Option { + match self.chain_config { + Either::Left(config) => Some(config), + Either::Right(_) => None, + } + } +} + +impl From> for ResolvableChainConfig { + fn from(value: Commitment) -> Self { + Self { + chain_config: Either::Right(value), + } + } +} + +impl From for ResolvableChainConfig { + fn from(value: ChainConfig) -> Self { + Self { + chain_config: Either::Left(value), + } + } +} + +pub type BlockMerkleTree = LightWeightSHA3MerkleTree>; +pub type BlockMerkleCommitment = ::Commitment; + +#[tagged("BUILDER_COMMITMENT")] +#[derive(Clone, Debug, Hash, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)] +/// Commitment that builders use to sign block options. +/// A thin wrapper around a Sha256 digest. +pub struct BuilderCommitment(Sha256Digest); + +impl AsRef for BuilderCommitment { + fn as_ref(&self) -> &Sha256Digest { + &self.0 + } +} + +pub enum Header { + V1(Header0_1), + V2(Header0_1), + V3(v0_3::Header), +} + +impl Header { + pub fn height(&self) -> u64 { + match self { + Header::V1(header0_1) => header0_1.height, + Header::V2(header0_1) => header0_1.height, + Header::V3(header) => header.height, + } + } +} + +impl<'de> Deserialize<'de> for Header { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct HeaderVisitor; + + impl<'de> Visitor<'de> for HeaderVisitor { + type Value = Header; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Header") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: SeqAccess<'de>, + { + let chain_config_or_version: EitherOrVersion = seq + .next_element()? + .ok_or_else(|| de::Error::missing_field("chain_config"))?; + + match chain_config_or_version { + // For v0.1, the first field in the sequence of fields is the first field of the struct, so we call a function to get the rest of + // the fields from the sequence and pack them into the struct. + EitherOrVersion::Left(cfg) => Ok(Header::V1( + Header0_1::deserialize_with_chain_config(cfg.into(), seq)?, + )), + EitherOrVersion::Right(commit) => Ok(Header::V1( + Header0_1::deserialize_with_chain_config(commit.into(), seq)?, + )), + // For all versions > 0.1, the first "field" is not actually part of the `Header` struct. + // We just delegate directly to the derived deserialization impl for the appropriate version. + EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2( + seq.next_element()? + .ok_or_else(|| de::Error::missing_field("fields"))?, + )), + EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3( + seq.next_element()? + .ok_or_else(|| de::Error::missing_field("fields"))?, + )), + EitherOrVersion::Version(v) => { + Err(serde::de::Error::custom(format!("invalid version {v:?}"))) + } + } + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + // insert all the fields in the serde_map as the map may have out of order fields. + let mut serde_map: Map = Map::new(); + + while let Some(key) = map.next_key::()? { + serde_map.insert(key.trim().to_owned(), map.next_value()?); + } + + if let Some(v) = serde_map.get("version") { + let fields = serde_map + .get("fields") + .ok_or_else(|| de::Error::missing_field("fields"))?; + + let version = serde_json::from_value::(v.clone()) + .map_err(de::Error::custom)?; + let result = match version { + EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2( + serde_json::from_value(fields.clone()).map_err(de::Error::custom)?, + )), + EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3( + serde_json::from_value(fields.clone()).map_err(de::Error::custom)?, + )), + EitherOrVersion::Version(v) => { + Err(de::Error::custom(format!("invalid version {v:?}"))) + } + chain_config => Err(de::Error::custom(format!( + "expected version, found chain_config {chain_config:?}" + ))), + }; + return result; + } + + Ok(Header::V1( + serde_json::from_value(serde_map.into()).map_err(de::Error::custom)?, + )) + } + } + + // List of all possible fields of all versions of the `Header`. + // serde's `deserialize_struct` works by deserializing to a struct with a specific list of fields. + // The length of the fields list we provide is always going to be greater than the length of the target struct. + // In our case, we are deserializing to either a V1 Header or a VersionedHeader for versions > 0.1. + // We use serde_json and bincode serialization in the sequencer. + // Fortunately, serde_json ignores fields parameter and only cares about our Visitor implementation. + // - https://docs.rs/serde_json/1.0.120/serde_json/struct.Deserializer.html#method.deserialize_struct + // Bincode uses the length of the fields list, but the bincode deserialization only cares that the length of the fields + // is an upper bound of the target struct's fields length. + // - https://docs.rs/bincode/1.3.3/src/bincode/de/mod.rs.html#313 + // This works because the bincode deserializer only consumes the next field when `next_element` is called, + // and our visitor calls it the correct number of times. + // This would, however, break if the bincode deserializer implementation required an exact match of the field's length, + // consuming one element for each field. + let fields: &[&str] = &[ + "fields", + "chain_config", + "version", + "height", + "timestamp", + "l1_head", + "l1_finalized", + "payload_commitment", + "builder_commitment", + "ns_table", + "block_merkle_tree_root", + "fee_merkle_tree_root", + "fee_info", + "builder_signature", + ]; + + deserializer.deserialize_struct("Header", fields, HeaderVisitor) + } +} + +impl Committable for Header { + fn commit(&self) -> Commitment { + match self { + Self::V1(header) => header.commit(), + Self::V2(fields) => RawCommitmentBuilder::new(&Self::tag()) + .u64_field("version_major", 0) + .u64_field("version_minor", 2) + .field("fields", fields.commit()) + .finalize(), + Self::V3(fields) => RawCommitmentBuilder::new(&Self::tag()) + .u64_field("version_major", 0) + .u64_field("version_minor", 3) + .field("fields", fields.commit()) + .finalize(), + } + } + + fn tag() -> String { + // We use the tag "BLOCK" since blocks are identified by the hash of their header. This will + // thus be more intuitive to users than "HEADER". + "BLOCK".into() + } +} + +impl Serialize for Header { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::V1(header) => header.serialize(serializer), + Self::V2(fields) => VersionedHeader { + version: EitherOrVersion::Version(Version { major: 0, minor: 2 }), + fields: fields.clone(), + } + .serialize(serializer), + Self::V3(fields) => VersionedHeader { + version: EitherOrVersion::Version(Version { major: 0, minor: 3 }), + fields: fields.clone(), + } + .serialize(serializer), + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct VersionedHeader { + pub(crate) version: EitherOrVersion, + pub(crate) fields: Fields, +} + +#[derive(Deserialize, Serialize, Debug)] +pub enum EitherOrVersion { + Left(ChainConfig), + Right(Commitment), + Version(Version), +} + +// Header values +#[derive(Clone, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)] +pub struct Header0_1 { + pub chain_config: ResolvableChainConfig, + pub height: u64, + pub timestamp: u64, + pub l1_head: u64, + pub l1_finalized: Option, + pub payload_commitment: VidCommitment, + pub builder_commitment: BuilderCommitment, + pub ns_table: NsTable, + pub block_merkle_tree_root: BlockMerkleCommitment, + pub fee_merkle_tree_root: FeeMerkleCommitment, + pub builder_signature: Option, + pub fee_info: FeeInfo, +} + +impl Header0_1 { + fn commit(&self) -> Commitment
{ + let mut bmt_bytes = vec![]; + self.block_merkle_tree_root + .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes) + .unwrap(); + let mut fmt_bytes = vec![]; + self.fee_merkle_tree_root + .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes) + .unwrap(); + + RawCommitmentBuilder::new(&Self::tag()) + .field("chain_config", self.chain_config.commit()) + .u64_field("height", self.height) + .u64_field("timestamp", self.timestamp) + .u64_field("l1_head", self.l1_head) + .optional("l1_finalized", &self.l1_finalized) + .constant_str("payload_commitment") + .fixed_size_bytes(self.payload_commitment.as_ref().as_ref()) + .constant_str("builder_commitment") + .fixed_size_bytes(self.builder_commitment.as_ref()) + .field("ns_table", self.ns_table.commit()) + .var_size_field("block_merkle_tree_root", &bmt_bytes) + .var_size_field("fee_merkle_tree_root", &fmt_bytes) + .field("fee_info", self.fee_info.commit()) + .finalize() + } + + fn tag() -> String { + // We use the tag "BLOCK" since blocks are identified by the hash of their header. This will + // thus be more intuitive to users than "HEADER". + "BLOCK".into() + } +} + +impl Header0_1 { + pub fn deserialize_with_chain_config<'de, A>( + chain_config: ResolvableChainConfig, + mut seq: A, + ) -> Result + where + A: SeqAccess<'de>, + { + macro_rules! element { + ($seq:expr, $field:ident) => { + $seq.next_element()? + .ok_or_else(|| de::Error::missing_field(stringify!($field)))? + }; + } + let height = element!(seq, height); + let timestamp = element!(seq, timestamp); + let l1_head = element!(seq, l1_head); + let l1_finalized = element!(seq, l1_finalized); + let payload_commitment = element!(seq, payload_commitment); + let builder_commitment = element!(seq, builder_commitment); + let ns_table = element!(seq, ns_table); + let block_merkle_tree_root = element!(seq, block_merkle_tree_root); + let fee_merkle_tree_root = element!(seq, fee_merkle_tree_root); + let fee_info = element!(seq, fee_info); + let builder_signature = element!(seq, builder_signature); + + Ok(Self { + chain_config, + height, + timestamp, + l1_head, + l1_finalized, + payload_commitment, + builder_commitment, + ns_table, + block_merkle_tree_root, + fee_merkle_tree_root, + fee_info, + builder_signature, + }) + } +} + +/// Type for protocol version number +#[derive(Deserialize, Serialize, Debug)] +pub struct Version { + /// major version number + pub major: u16, + /// minor version number + pub minor: u16, +} + +pub type FeeMerkleTree = UniversalMerkleTree; +pub type FeeMerkleCommitment = ::Commitment; + +/// Type alias for byte array of SHA256 digest length +type Sha256Digest = [u8; ::OutputSize::USIZE]; + +#[derive(Default, Hash, Copy, Clone, Debug, PartialEq, Eq, Add, Sub, From, Into, Display)] +#[display(fmt = "{_0}")] +pub struct FeeAmount(U256); + +impl FromStringOrInteger for FeeAmount { + type Binary = U256; + type Integer = u64; + + fn from_binary(b: Self::Binary) -> Result { + Ok(Self(b)) + } + + fn from_integer(i: Self::Integer) -> Result { + Ok(i.into()) + } + + fn from_string(s: String) -> Result { + // For backwards compatibility, we have an ad hoc parser for WEI amounts represented as hex + // strings. + if let Some(s) = s.strip_prefix("0x") { + return Ok(Self(s.parse().unwrap())); + } + + // Strip an optional non-numeric suffix, which will be interpreted as a unit. + let (base, unit) = s + .split_once(char::is_whitespace) + .unwrap_or((s.as_str(), "wei")); + match parse_units(base, unit).unwrap() { + ParseUnits::U256(n) => Ok(Self(n)), + ParseUnits::I256(_) => panic!("amount cannot be negative"), + } + } + + fn to_binary(&self) -> Result { + Ok(self.0) + } + + fn to_string(&self) -> Result { + Ok(format!("{self}")) + } +} + +impl_serde_from_string_or_integer!(FeeAmount); +impl_to_fixed_bytes!(FeeAmount, U256); + +impl From for FeeAmount { + fn from(amt: u64) -> Self { + Self(amt.into()) + } +} + +impl CanonicalSerialize for FeeAmount { + fn serialize_with_mode( + &self, + mut writer: W, + _compress: Compress, + ) -> Result<(), SerializationError> { + Ok(writer.write_all(&self.to_fixed_bytes())?) + } + + fn serialized_size(&self, _compress: Compress) -> usize { + core::mem::size_of::() + } +} +impl CanonicalDeserialize for FeeAmount { + fn deserialize_with_mode( + mut reader: R, + _compress: Compress, + _validate: Validate, + ) -> Result { + let mut bytes = [0u8; core::mem::size_of::()]; + reader.read_exact(&mut bytes)?; + let value = U256::from_little_endian(&bytes); + Ok(Self(value)) + } +} + +impl CanonicalSerialize for FeeAccount { + fn serialize_with_mode( + &self, + mut writer: W, + _compress: Compress, + ) -> Result<(), SerializationError> { + Ok(writer.write_all(&self.0.to_fixed_bytes())?) + } + + fn serialized_size(&self, _compress: Compress) -> usize { + core::mem::size_of::
() + } +} + +impl CanonicalDeserialize for FeeAccount { + fn deserialize_with_mode( + mut reader: R, + _compress: Compress, + _validate: Validate, + ) -> Result { + let mut bytes = [0u8; core::mem::size_of::
()]; + reader.read_exact(&mut bytes)?; + let value = Address::from_slice(&bytes); + Ok(Self(value)) + } +} + +#[derive( + Default, + Hash, + Copy, + Clone, + Debug, + Display, + Deserialize, + Serialize, + PartialEq, + Eq, + PartialOrd, + Ord, + From, + Into, +)] +#[display(fmt = "{_0:x}")] +pub struct FeeAccount(Address); + +impl FeeAccount { + /// Return inner `Address` + pub fn address(&self) -> Address { + self.0 + } + /// Return byte slice representation of inner `Address` type + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + /// Return array containing underlying bytes of inner `Address` type + pub fn to_fixed_bytes(self) -> [u8; 20] { + self.0.to_fixed_bytes() + } +} + +impl ToTraversalPath<256> for FeeAccount { + fn to_traversal_path(&self, height: usize) -> Vec { + self.0 + .to_fixed_bytes() + .into_iter() + .take(height) + .map(|i| i as usize) + .collect() + } +} + +impl Valid for FeeAmount { + fn check(&self) -> Result<(), SerializationError> { + Ok(()) + } +} + +impl Valid for FeeAccount { + fn check(&self) -> Result<(), SerializationError> { + Ok(()) + } +} + +#[derive( + Hash, + Copy, + Clone, + Debug, + Deserialize, + Serialize, + PartialEq, + Eq, + CanonicalSerialize, + CanonicalDeserialize, +)] +pub struct FeeInfo { + account: FeeAccount, + amount: FeeAmount, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Hash, PartialEq, Eq)] +pub struct L1BlockInfo { + pub number: u64, + pub timestamp: U256, + pub hash: H256, +} + +impl Committable for L1BlockInfo { + fn commit(&self) -> Commitment { + let mut timestamp = [0u8; 32]; + self.timestamp.to_little_endian(&mut timestamp); + + RawCommitmentBuilder::new(&Self::tag()) + .u64_field("number", self.number) + // `RawCommitmentBuilder` doesn't have a `u256_field` method, so we simulate it: + .constant_str("timestamp") + .fixed_size_bytes(×tamp) + .constant_str("hash") + .fixed_size_bytes(&self.hash.0) + .finalize() + } + + fn tag() -> String { + "L1BLOCK".into() + } +} + +impl Committable for FeeInfo { + fn commit(&self) -> Commitment { + RawCommitmentBuilder::new(&Self::tag()) + .fixed_size_field("account", &self.account.to_fixed_bytes()) + .fixed_size_field("amount", &self.amount.to_fixed_bytes()) + .finalize() + } + fn tag() -> String { + "FEE_INFO".into() + } +} + +pub fn field_to_u256(f: F) -> U256 { + if F::MODULUS_BIT_SIZE > 256 { + panic!("Shouldn't convert a >256-bit field to U256"); + } + U256::from_little_endian(&f.into_bigint().to_bytes_le()) +} + +#[cfg(test)] +mod tests { + + use super::Header0_1; + + #[test] + fn header_test() { + let header_str = include_str!("./mock_data/header.json"); + let header = serde_json::from_str::(&header_str).unwrap(); + // Copied from espresso sequencer reference test + let expected = "BLOCK~6Ol30XYkdKaNFXw0QAkcif18Lk8V8qkC4M81qTlwL707"; + assert_eq!(header.commit().to_string(), expected); + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/uint_bytes.rs b/espressocrypto/lib/espresso-crypto-helper/src/uint_bytes.rs new file mode 100644 index 0000000000..a1bd0ea49d --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/uint_bytes.rs @@ -0,0 +1,107 @@ +//! Serialization (and deserialization) of primitive unsigned integer types to +//! (and from) an arbitrary fixed-length byte array. +//! +use paste::paste; +use std::mem::size_of; + +// Use an ugly macro because it's difficult or impossible to be generic over +// primitive types such as `usize`, `u64`. +macro_rules! uint_bytes_impl { + ($T:ty) => { + paste! { + /// Serialize `n` into `BYTE_LEN` bytes in little-endian form, padding with + /// 0 as needed. + /// + /// # Panics + /// If `n` cannot fit into `BYTE_LEN` bytes. + #[allow(dead_code)] + pub fn [<$T _to_bytes>](n: $T) -> [u8; BYTE_LEN] { + if size_of::<$T>() > BYTE_LEN { + assert!( + [<$T _fits>](n, BYTE_LEN), + "n {n} cannot fit into {BYTE_LEN} bytes" + ); + n.to_le_bytes()[..BYTE_LEN].try_into().unwrap() // panic is impossible + } else { + // convert `n` to bytes and pad with 0 + let mut result = [0; BYTE_LEN]; + result[..size_of::<$T>()].copy_from_slice(&n.to_le_bytes()[..]); + result + } + } + + /// Deserialize `bytes` in little-endian form into a `$T`, padding with 0 + /// as needed. + /// + /// # Panics + /// If `bytes.len()` is too large to fit into a `$T`. + pub fn [<$T _from_bytes>](bytes: &[u8]) -> $T { + assert!(bytes.len() <= BYTE_LEN, "bytes len {} exceeds BYTE_LEN {BYTE_LEN}", bytes.len()); + assert!( + BYTE_LEN <= size_of::<$T>(), + "BYTE_LEN {BYTE_LEN} cannot fit into {}", + stringify!($T) + ); + let mut [<$T _bytes>] = [0; size_of::<$T>()]; + [<$T _bytes>][..bytes.len()].copy_from_slice(bytes); + $T::from_le_bytes([<$T _bytes>]) + } + + /// Return the largest `$T` value that can fit into `byte_len` bytes. + pub const fn [<$T _max_from_byte_len>](byte_len: usize) -> $T { + if byte_len >= size_of::<$T>() { + $T::MAX + } else { + // overflow cannot occur because `byte_len < size_of::<$T>()` + (1 << (byte_len * 8)) - 1 + } + } + + /// Can `n` fit into `byte_len` bytes? + pub const fn [<$T _fits>](n: $T, byte_len: usize) -> bool { + n <= [<$T _max_from_byte_len>](byte_len) + } + } + }; + } + +uint_bytes_impl!(usize); +uint_bytes_impl!(u32); + +/// Impl [`serde`] for type `$T` with methods named `$to_bytes`, `$from_bytes` +/// of the form +/// ```ignore +/// $T::$to_bytes(&self) -> $B +/// $T::$from_bytes(bytes: &[u8]) -> Self +/// ``` +/// where `$B` is any type that impls [`serde::Deserialize`] and has a method +/// `as_ref` of the form +/// ```ignore +/// $B::as_ref(&self) -> &[u8] +/// ``` +/// Typical examples of `$B` include array `[u8; N]`, slice `&[u8]`, or +/// `Vec`. +macro_rules! bytes_serde_impl { + ($T:ty, $to_bytes:ident, $B:ty, $from_bytes:ident) => { + impl Serialize for $T { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.$to_bytes().serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $T { + fn deserialize(deserializer: D) -> Result<$T, D::Error> + where + D: Deserializer<'de>, + { + <$B as Deserialize>::deserialize(deserializer) + .map(|bytes| <$T>::$from_bytes(bytes.as_ref())) + } + } + }; +} + +pub(super) use bytes_serde_impl; diff --git a/espressocrypto/lib/espresso-crypto-helper/src/utils.rs b/espressocrypto/lib/espresso-crypto-helper/src/utils.rs new file mode 100644 index 0000000000..23bd000055 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/utils.rs @@ -0,0 +1,75 @@ +use serde::{ + de::{DeserializeOwned, Deserializer, Error as _}, + ser::Serializer, + Deserialize, Serialize, +}; + +pub type Err = u8; + +/// Basically copied from sequencer repo. Just removed the error types to avoid introducing other crate +pub trait FromStringOrInteger: Sized { + type Binary: Serialize + DeserializeOwned; + type Integer: Serialize + DeserializeOwned; + + fn from_binary(b: Self::Binary) -> Result; + fn from_string(s: String) -> Result; + fn from_integer(i: Self::Integer) -> Result; + + fn to_binary(&self) -> Result; + fn to_string(&self) -> Result; +} + +#[macro_export] +macro_rules! impl_serde_from_string_or_integer { + ($t:ty) => { + impl serde::Serialize for $t { + fn serialize(&self, s: S) -> Result { + $crate::utils::string_or_integer::serialize(self, s) + } + } + + impl<'de> serde::Deserialize<'de> for $t { + fn deserialize>(d: D) -> Result { + $crate::utils::string_or_integer::deserialize(d) + } + } + }; +} + +pub use crate::impl_serde_from_string_or_integer; + +pub mod string_or_integer { + + use super::*; + + #[derive(Debug, Deserialize)] + #[serde(untagged)] + enum StringOrInteger { + String(String), + Integer(I), + } + + pub fn serialize( + t: &T, + s: S, + ) -> Result { + if s.is_human_readable() { + t.to_string().unwrap().serialize(s) + } else { + t.to_binary().unwrap().serialize(s) + } + } + + pub fn deserialize<'a, T: FromStringOrInteger, D: Deserializer<'a>>( + d: D, + ) -> Result { + if d.is_human_readable() { + match StringOrInteger::deserialize(d)? { + StringOrInteger::String(s) => T::from_string(s).map_err(D::Error::custom), + StringOrInteger::Integer(i) => T::from_integer(i).map_err(D::Error::custom), + } + } else { + T::from_binary(T::Binary::deserialize(d)?).map_err(D::Error::custom) + } + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/v0_3/auction.rs b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/auction.rs new file mode 100644 index 0000000000..9c3209c89e --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/auction.rs @@ -0,0 +1,130 @@ +use super::super::hotshot_types::ViewNumber; +use super::{FeeAccount, FeeAmount}; +use crate::NamespaceId; +use committable::{Commitment, Committable}; +use ethers_core::types::Signature; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)] +/// Wrapper enum for Full Network Transactions. Each transaction type +/// will be a variant of this enum. +pub enum FullNetworkTx { + Bid(BidTx), +} + +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)] +/// A transaction to bid for the sequencing rights of a namespace. It +/// is the `signed` form of `BidTxBody`. Expected usage is *build* +/// it by calling `signed` on `BidTxBody`. +pub struct BidTx { + pub(crate) body: BidTxBody, + pub(crate) signature: Signature, +} + +/// A transaction body holding data required for bid submission. +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)] +pub struct BidTxBody { + /// Account responsible for the signature + pub(crate) account: FeeAccount, + /// Fee to be sequenced in the network. Different than the bid_amount fee + // FULL_NETWORK_GAS * MINIMUM_GAS_PRICE + pub(crate) gas_price: FeeAmount, + /// The bid amount designated in Wei. This is different than + /// the sequencing fee (gas price) for this transaction + pub(crate) bid_amount: FeeAmount, + /// The URL the HotShot leader will use to request a bundle + /// from this sequencer if they win the auction + pub(crate) url: String, + /// The slot this bid is for + pub(crate) view: ViewNumber, + /// The set of namespace ids the sequencer is bidding for + pub(crate) namespaces: Vec, +} + +/// The results of an Auction +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)] +pub struct SolverAuctionResults { + /// view number the results are for + pub(crate) view_number: ViewNumber, + /// A list of the bid txs that won + pub(crate) winning_bids: Vec, + /// A list of reserve sequencers being used + pub(crate) reserve_bids: Vec<(NamespaceId, String)>, +} + +impl Committable for SolverAuctionResults { + fn tag() -> String { + "SOLVER_AUCTION_RESULTS".to_string() + } + + fn commit(&self) -> Commitment { + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) + .fixed_size_field("view_number", &self.view_number.commit().into()) + .array_field( + "winning_bids", + &self + .winning_bids + .iter() + .map(Committable::commit) + .collect::>(), + ) + .array_field( + "reserve_bids", + &self + .reserve_bids + .iter() + .map(|(nsid, url)| { + // Set a phantom type to make the compiler happy + committable::RawCommitmentBuilder::::new( + "RESERVE_BID", + ) + .u64(nsid.0) + .constant_str(url.as_str()) + .finalize() + }) + .collect::>(), + ); + comm.finalize() + } +} + +impl Committable for BidTx { + fn tag() -> String { + "BID_TX".to_string() + } + + fn commit(&self) -> Commitment { + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) + .field("body", self.body.commit()) + .fixed_size_field("signature", &self.signature.into()); + comm.finalize() + } +} + +impl Committable for BidTxBody { + fn tag() -> String { + "BID_TX_BODY".to_string() + } + + fn commit(&self) -> Commitment { + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) + .fixed_size_field("account", &self.account.to_fixed_bytes()) + .fixed_size_field("gas_price", &self.gas_price.to_fixed_bytes()) + .fixed_size_field("bid_amount", &self.bid_amount.to_fixed_bytes()) + .var_size_field("url", self.url.as_str().as_ref()) + .u64_field("view", self.view.0) + .array_field( + "namespaces", + &self + .namespaces + .iter() + .map(|e| { + committable::RawCommitmentBuilder::::new("namespace") + .u64(e.0) + .finalize() + }) + .collect::>(), + ); + comm.finalize() + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/v0_3/chain_config.rs b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/chain_config.rs new file mode 100644 index 0000000000..47198d6115 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/chain_config.rs @@ -0,0 +1,109 @@ +use super::{BlockSize, ChainId, FeeAccount, FeeAmount}; +use committable::{Commitment, Committable}; +use ethers_core::types::{Address, U256}; +use itertools::Either; +use serde::{Deserialize, Serialize}; + +/// Global variables for an Espresso blockchain. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ChainConfig { + /// Espresso chain ID + pub chain_id: ChainId, + + /// Maximum size in bytes of a block + pub max_block_size: BlockSize, + + /// Minimum fee in WEI per byte of payload + pub base_fee: FeeAmount, + + /// Fee contract address on L1. + /// + /// This is optional so that fees can easily be toggled on/off, with no need to deploy a + /// contract when they are off. In a future release, after fees are switched on and thoroughly + /// tested, this may be made mandatory. + pub fee_contract: Option
, + + /// Account that receives sequencing fees. + /// + /// This account in the Espresso fee ledger will always receive every fee paid in Espresso, + /// regardless of whether or not their is a `fee_contract` deployed. Once deployed, the fee + /// contract can decide what to do with tokens locked in this account in Espresso. + pub fee_recipient: FeeAccount, + + /// Account that receives sequencing bids. + pub bid_recipient: Option, +} + +#[derive(Clone, Debug, Copy, PartialEq, Deserialize, Serialize, Eq, Hash)] +/// A commitment to a ChainConfig or a full ChainConfig. +pub struct ResolvableChainConfig { + pub(crate) chain_config: Either>, +} + +impl Committable for ChainConfig { + fn tag() -> String { + "CHAIN_CONFIG".to_string() + } + + fn commit(&self) -> Commitment { + let comm = committable::RawCommitmentBuilder::new(&Self::tag()) + .fixed_size_field("chain_id", &self.chain_id.to_fixed_bytes()) + .u64_field("max_block_size", *self.max_block_size) + .fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes()) + .fixed_size_field("fee_recipient", &self.fee_recipient.to_fixed_bytes()); + let comm = if let Some(addr) = self.fee_contract { + comm.u64_field("fee_contract", 1).fixed_size_bytes(&addr.0) + } else { + comm.u64_field("fee_contract", 0) + }; + + // With `ChainConfig` upgrades we want commitments w/out + // fields added >= v0_3 to have the same commitment as <= v0_3 + // commitment. Therefore `None` values are simply ignored. + let comm = if let Some(bid_recipient) = self.bid_recipient { + comm.fixed_size_field("bid_recipient", &bid_recipient.to_fixed_bytes()) + } else { + comm + }; + + comm.finalize() + } +} + +impl ResolvableChainConfig { + pub fn commit(&self) -> Commitment { + match self.chain_config { + Either::Left(config) => config.commit(), + Either::Right(commitment) => commitment, + } + } +} + +impl From> for ResolvableChainConfig { + fn from(value: Commitment) -> Self { + Self { + chain_config: Either::Right(value), + } + } +} + +impl From for ResolvableChainConfig { + fn from(value: ChainConfig) -> Self { + Self { + chain_config: Either::Left(value), + } + } +} + +impl Default for ChainConfig { + fn default() -> Self { + Self { + chain_id: U256::from(35353).into(), // arbitrarily chosen chain ID + max_block_size: 30720.into(), + base_fee: 0.into(), + fee_contract: None, + fee_recipient: Default::default(), + bid_recipient: None, + } + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/v0_3/header.rs b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/header.rs new file mode 100644 index 0000000000..ef88bb666a --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/header.rs @@ -0,0 +1,70 @@ +use super::{ + BlockMerkleCommitment, FeeInfo, FeeMerkleCommitment, L1BlockInfo, ResolvableChainConfig, + SolverAuctionResults, +}; +use crate::hotshot_types::{BuilderCommitment, VidCommitment}; +use crate::NsTable; +use ark_serialize::CanonicalSerialize; +use committable::{Commitment, Committable, RawCommitmentBuilder}; +use ethers_core::types::Signature as BuilderSignature; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)] +/// A header is like a [`Block`] with the body replaced by a digest. +pub struct Header { + /// A commitment to a ChainConfig or a full ChainConfig. + pub(crate) chain_config: ResolvableChainConfig, + pub(crate) height: u64, + pub(crate) timestamp: u64, + pub(crate) l1_head: u64, + pub(crate) l1_finalized: Option, + pub(crate) payload_commitment: VidCommitment, + pub(crate) builder_commitment: BuilderCommitment, + pub(crate) ns_table: NsTable, + pub(crate) block_merkle_tree_root: BlockMerkleCommitment, + pub(crate) fee_merkle_tree_root: FeeMerkleCommitment, + pub(crate) fee_info: Vec, + pub(crate) builder_signature: Vec, + pub(crate) auction_results: SolverAuctionResults, +} + +impl Committable for Header { + fn commit(&self) -> Commitment { + let mut bmt_bytes = vec![]; + self.block_merkle_tree_root + .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes) + .unwrap(); + let mut fmt_bytes = vec![]; + self.fee_merkle_tree_root + .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes) + .unwrap(); + + RawCommitmentBuilder::new(&Self::tag()) + .field("chain_config", self.chain_config.commit()) + .u64_field("height", self.height) + .u64_field("timestamp", self.timestamp) + .u64_field("l1_head", self.l1_head) + .optional("l1_finalized", &self.l1_finalized) + .constant_str("payload_commitment") + .fixed_size_bytes(self.payload_commitment.as_ref().as_ref()) + .constant_str("builder_commitment") + .fixed_size_bytes(self.builder_commitment.as_ref()) + .field("ns_table", self.ns_table.commit()) + .var_size_field("block_merkle_tree_root", &bmt_bytes) + .var_size_field("fee_merkle_tree_root", &fmt_bytes) + .array_field( + "fee_info", + &self + .fee_info + .iter() + .map(Committable::commit) + .collect::>(), + ) + .field("auction_results", self.auction_results.commit()) + .finalize() + } + + fn tag() -> String { + "BLOCK".into() + } +} diff --git a/espressocrypto/lib/espresso-crypto-helper/src/v0_3/mod.rs b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/mod.rs new file mode 100644 index 0000000000..e568b23500 --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/src/v0_3/mod.rs @@ -0,0 +1,13 @@ +// Re-export types which haven't changed since the last minor version. +pub use super::sequencer_data_structures::{ + BlockMerkleCommitment, BlockSize, ChainId, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment, + L1BlockInfo, +}; + +mod auction; +mod chain_config; +mod header; + +pub use auction::SolverAuctionResults; +pub use chain_config::*; +pub use header::Header; diff --git a/espressocrypto/lib/espresso-crypto-helper/vid_srs.json b/espressocrypto/lib/espresso-crypto-helper/vid_srs.json new file mode 100644 index 0000000000..aa0532dceb --- /dev/null +++ b/espressocrypto/lib/espresso-crypto-helper/vid_srs.json @@ -0,0 +1 @@ +[5,0,0,0,0,0,0,0,1,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,2,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,249,230,49,207,189,34,111,38,253,239,131,186,97,251,231,171,98,78,190,123,168,209,107,255,67,249,159,40,40,6,54,45,170,10,112,5,212,123,240,59,100,176,247,78,203,61,211,248,240,162,236,254,231,204,82,130,244,195,99,229,121,42,185,166,219,137,228,207,21,151,128,212,130,59,138,190,25,73,54,189,135,81,200,124,137,67,158,163,166,72,205,60,228,214,10,2,67,69,158,23,152,197,51,106,108,140,24,44,111,20,97,192,10,96,210,7,20,82,156,180,146,61,234,226,186,158,167,161,118,140,215,125,138,226,57,182,7,222,71,91,232,103,156,194,54,190,135,208,45,8,92,144,114,46,122,185,203,237,11,33,101,226,121,255,196,55,110,254,210,246,234,12,28,192,114,52,238,47,69,188,187,4,105,47,171,163,58,184,49,228,209,152,214,96,132,233,17,76,177,73,144,41,127,136,58,178,129,238,30,130,27,98,71,103,194,185,19,190,176,246,65,206,86,42,124,44,239,166,174,161,44,209,192,86,134,199,56,99,125,130,213,222,37,231,228,109,143,125,84,218,221,227,45,143,169,154,237,246,146,217,92,189,222,70,221,218,94,247,212,34,67,103,121,68,92,94,102,0,106,66,118,30,31,18,239,222,0,24,194,18,243,174,183,133,228,151,18,231,169,53,51,73,170,241,37,93,251,49,183,191,96,114,58,72,13,146,147,147,142,25,170,125,250,102,1,204,230,76,123,211,67,12,105,231,209,227,143,64,203,141,128,113,171,74,235,109,140,219,165,94,200,18,91,151,34,209,220,218,172,85,243,142,179,112,51,49,75,188,149,51,12,105,173,153,158,236,117,240,95,88,208,137,6,9,176,131,136,147,236,31,35,126,139,7,50,59,7,68,89,159,78,151,181,152,179,181,137,188,194,188,55,184,213,196,24,1,193,131,147,192,250,48,254,78,139,3,142,53,122,216,81,234,232,222,145,7,88,78,255,231,199,241,246,81,178,1,14,38,85,94,204,218,212,135,74,133,162,206,230,150,63,221,230,17,94,97,229,20,66,91,71,86,42,99,192,192,163,189,254,34,228,95,106,218,128,60,65,238,164,155,249,65,70,160,242,156,133,114,154,187,193,86,81,210,227,15,17,247,105,99,252,4,2,0,0,0,0,0,0,0,237,246,146,217,92,189,222,70,221,218,94,247,212,34,67,103,121,68,92,94,102,0,106,66,118,30,31,18,239,222,0,24,194,18,243,174,183,133,228,151,18,231,169,53,51,73,170,241,37,93,251,49,183,191,96,114,58,72,13,146,147,147,142,25,170,125,250,102,1,204,230,76,123,211,67,12,105,231,209,227,143,64,203,141,128,113,171,74,235,109,140,219,165,94,200,18,91,151,34,209,220,218,172,85,243,142,179,112,51,49,75,188,149,51,12,105,173,153,158,236,117,240,95,88,208,137,6,9,176,131,136,147,236,31,35,126,139,7,50,59,7,68,89,159,78,151,181,152,179,181,137,188,194,188,55,184,213,196,24,1,193,131,147,192,250,48,254,78,139,3,142,53,122,216,81,234,232,222,145,7,88,78,255,231,199,241,246,81,178,1,14,38,85,94,204,218,212,135,74,133,162,206,230,150,63,221,230,17,94,97,229,20,66,91,71,86,42,99,192,192,163,189,254,34,228,95,106,218,128,60,65,238,164,155,249,65,70,160,242,156,133,114,154,187,193,86,81,210,227,15,17,247,105,99,252,4] \ No newline at end of file diff --git a/espressocrypto/merkle_proof_test_data.json b/espressocrypto/merkle_proof_test_data.json new file mode 100644 index 0000000000..1b71079caf --- /dev/null +++ b/espressocrypto/merkle_proof_test_data.json @@ -0,0 +1,548 @@ +{ + "proof": [ + { + "Leaf": { + "value": "FIELD~jr9tXVfv2gkbW1-4wwTSiSOdjSgdAGtmFUsGGrdfwslH", + "pos": "FIELD~AQAAAAAAAADo", + "elem": "FIELD~gD6pmadEiJi4gaK_pB3B2iXqEhYEiNlDoPswEoUdcmH7" + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~3z9fN-iUUwPdvWoR7lDf1jh_PVzIkyewzyt4uPN-FNBB" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~jr9tXVfv2gkbW1-4wwTSiSOdjSgdAGtmFUsGGrdfwslH" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~t_QJoO2RY0cpjgCoL6oGOKzH7OJnbr0XD6xEfbQKAKcA" + } + } + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~xvAIt51EdmFWgZfMd8HOS4SeE0l_0NQA3Ri8lNFgV1HL" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~oM6IlzZYEr887TdMbUPwI6WHvz05WA9cSmtzGD_SlaQl" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~VuxjkuE6ScgO1Q-BgZqfcE74cruzZyHt3aAflPQA-xIB" + } + } + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~aE3umwJP5Wxby08dSbbvrePITT7XalvUnPSzJ-ZWng1T" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~8wH3jf5458KMdPkvyp9BukvERjdW6I99K_OTR0cafD4-" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~4GQkVV0earyB-RRsl-De5Ircta1ldHGswH2sQnGKnUD_" + } + } + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~rh6CE7pUqSw3se_qdktI3GrY811qRN8OfeAFbEivfIWk" + } + }, + { + "ForgettenSubtree": { + "value": "FIELD~O9MLnSq7qv8SB0hfyoxCRthe2UV_nNCME37ge_wk6xsN" + } + }, + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~-zeL-oXBCzJq6cVx2jrdqFe_J82_m-Oh2KhsK5Rqs8JP" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~06NzPztH3uT0ivUPWIjU9Tq9CQzN_laCBVDuTvLKGbon" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~mk4SayYZGjwHLQG0YoqwG7tGTrKFQMytZasnB5QN5UZB" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~dsWyix73TcK80q49oiGmEqzWqR8oIsfjReogVyAkfQ0P" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~FjpEExwINQ1Xgs_nDdSMOkuAYwo-lHCt1KrBXoFf_LSL" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~XGTceQX-msI2oyzw7PwvGKt4bOBgrTHXSAENr78_0U6p" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~epY9M7XPeUARbAhPSIIW8POLl5JCcBsn9t05edLzq5YG" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~q0E5nM1XPN9objZpxbAO-piW6cWJFEiXSh5A-CiQZmob" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~v4WDpolSDLR2lUqc2lYc8_zSMpzjRnq9Lh4kjE4Ay9R4" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~5SSV5BTgfnusxlm0OJBk20kupMePPBbBt___I73ZLy7P" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~Dwrc1SJVkdz7q8gK4-wtgoMdA2YUYZdDWJtQ-wzcPnRb" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~bGVDqphMtIR4F4qFuVb5Rzto3LZOLx66Ia8lElHlOhcY" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~I0YU9-e15EpDIDO1xUvbGi9NQFaTE0Ffm_EIS8QFMcWL" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~Yv7PkdU2ukKlQV0JeqsccvkOXB-jRmxNj9AaMuBEi_eJ" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~9G2nQWB5G91jFiPm3M2AV9oVv0cS-ngzeZE78GXc94YC" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~SW292cUyXRY9-8f2HyNLgfswekO7INibuCsaQApAbXNj" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~yfULzrVppgGolz_BKWEW2bDnB_M4cwBt7CjbedgPojs9" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~cugJL4YVJ2yXXM-DTWyr-2LcWXRBwd6wiwuq56afJfHQ" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~2kgFeaU_CFugCMCfeEEYYaJPCYJYM_6gXyoHEN4mZzTL" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~5ufZAucZW6KqJxtgovfiFFjLvFRKPYIXE3wYzmGhu9ZA" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~1_BLKIKAytZ9w8RdZtoQUysvumjS5H3vzUoZwf7doc8T" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~TW7Wibe7_6jcmFlxKJtaIBcjQc_LGXxEpllZ0ntdyaKi" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~uyvk_8lpqEYZ-dFJfJ0iOLd9ZUMd-9JqFRYj-3K0o8eb" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~qg9ryCpY6L2_N6zWXCUQBLzHeldYEZn3KH0-VNxvZM3w" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~BZXZ9d5k2eP3-RYFngPFyIEog1rEI_5D4ARIj6OvtKIe" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~WKdC3PVAQjQa8YPvpZLen1dwum7nEEtKgjzzkFzBATfA" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~ywnYExXQ8ta58-IoLSVielGmwOB6EuXTyP6qeuFXDIPL" + } + }, + "Empty", + "Empty" + ] + } + }, + { + "Branch": { + "value": "FIELD~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-", + "children": [ + { + "ForgettenSubtree": { + "value": "FIELD~xT9olWHA7rnuJUY9P23gQ7fYpnA39j_9XaIbi3XTfDdo" + } + }, + "Empty", + "Empty" + ] + } + } + ], + "header": { + "version": { "Version": { "major": 0, "minor": 3 } }, + "fields": { + "chain_config": { + "chain_config": { + "Left": { + "chain_id": "35353", + "max_block_size": "30720", + "base_fee": "0", + "fee_contract": null, + "fee_recipient": "0x0000000000000000000000000000000000000000", + "bid_recipient": null + } + } + }, + "height": 1, + "timestamp": 1730161780, + "l1_head": 1, + "l1_finalized": { + "number": 0, + "timestamp": "0x67202c64", + "hash": "0x00699408692568ff48c468b4fde0063209d1eaaaef715f4cb5a5ace9dd8726b1" + }, + "payload_commitment": "HASH~L80VG4KV6LO_jsZO3hc1I0F5YKjbbLxWnemiWkWPkTU5", + "builder_commitment": "BUILDER_COMMITMENT~tEvs0rxqOiMCvfe2R0omNNaphSlUiEDrb2q0IZpRcgA_", + "ns_table": { "bytes": "AAAAAA==" }, + "block_merkle_tree_root": "MERKLE_COMM~H_rqdvlVRna8PtqiF4-LktNw4xglVy97IYbaHKXcUycgAAAAAAAAAAEAAAAAAAAA2Q", + "fee_merkle_tree_root": "MERKLE_COMM~XZo7X2To_xdKCieITCYOQyB3jDcOVsIcY_mFbZlH6NAUAAAAAAAAAAEAAAAAAAAArw", + "fee_info": [ + { + "account": "0xb0cfa4e5893107e2995974ef032957752bb526e9", + "amount": "0" + } + ], + "builder_signature": [ + { + "r": "0x1ed4ce0884520709271535bbef06a114ea47236935148ee724fd6bb33c693b96", + "s": "0x66c2148840fd27b97639eac03e309781e824baae6dd5aface1f356184f676fc3", + "v": 27 + } + ], + "auction_results": { + "view_number": 0, + "winning_bids": [], + "reserve_bids": [] + } + } + }, + "header_commitment": [ + 128, 62, 169, 153, 167, 68, 136, 152, 184, 129, 162, 191, 164, 29, 193, 218, + 37, 234, 18, 22, 4, 136, 217, 67, 160, 251, 48, 18, 133, 29, 114, 97 + ], + "block_merkle_root": "MERKLE_COMM~hgo2QfXdj-YpouFIHiVJQtU2Gtgrwjr3i1F_kmspKH8gAAAAAAAAACwAAAAAAAAArg", + "block_merkle_root_bytes": [ + 134, 10, 54, 65, 245, 221, 143, 230, 41, 162, 225, 72, 30, 37, 73, 66, 213, + 54, 26, 216, 43, 194, 58, 247, 139, 81, 127, 146, 107, 41, 40, 127 + ], + "hotshot_commitment": [ + 31, 49, 179, 162, 135, 255, 147, 182, 228, 48, 119, 184, 251, 157, 220, 207, + 126, 129, 194, 197, 75, 82, 196, 105, 6, 127, 201, 10, 127, 168, 37, 41 + ] +} diff --git a/espressocrypto/namespace_proof_test_data.json b/espressocrypto/namespace_proof_test_data.json new file mode 100644 index 0000000000..71203aa6a4 --- /dev/null +++ b/espressocrypto/namespace_proof_test_data.json @@ -0,0 +1,593 @@ +{ + "ns_proof": [ + 123, + 34, + 110, + 115, + 95, + 105, + 110, + 100, + 101, + 120, + 34, + 58, + 91, + 49, + 44, + 48, + 44, + 48, + 44, + 48, + 93, + 44, + 34, + 110, + 115, + 95, + 112, + 97, + 121, + 108, + 111, + 97, + 100, + 34, + 58, + 34, + 65, + 119, + 65, + 65, + 65, + 65, + 69, + 65, + 65, + 65, + 65, + 68, + 65, + 65, + 65, + 65, + 66, + 103, + 65, + 65, + 65, + 66, + 81, + 97, + 43, + 109, + 57, + 73, + 75, + 65, + 61, + 61, + 34, + 44, + 34, + 110, + 115, + 95, + 112, + 114, + 111, + 111, + 102, + 34, + 58, + 123, + 34, + 112, + 114, + 101, + 102, + 105, + 120, + 95, + 101, + 108, + 101, + 109, + 115, + 34, + 58, + 34, + 70, + 73, + 69, + 76, + 68, + 126, + 65, + 81, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 68, + 65, + 65, + 65, + 65, + 66, + 65, + 65, + 65, + 65, + 65, + 107, + 65, + 65, + 65, + 65, + 80, + 65, + 65, + 65, + 65, + 101, + 118, + 85, + 82, + 51, + 74, + 55, + 90, + 54, + 110, + 116, + 54, + 78, + 109, + 85, + 56, + 86, + 112, + 76, + 75, + 65, + 68, + 103, + 34, + 44, + 34, + 115, + 117, + 102, + 102, + 105, + 120, + 95, + 101, + 108, + 101, + 109, + 115, + 34, + 58, + 34, + 70, + 73, + 69, + 76, + 68, + 126, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 68, + 55, + 34, + 44, + 34, + 112, + 114, + 101, + 102, + 105, + 120, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 91, + 93, + 44, + 34, + 115, + 117, + 102, + 102, + 105, + 120, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 91, + 93, + 125, + 125 + ], + "vid_commit": [ + 72, + 65, + 83, + 72, + 126, + 73, + 111, + 104, + 90, + 122, + 71, + 67, + 115, + 76, + 89, + 99, + 51, + 72, + 113, + 104, + 115, + 56, + 112, + 121, + 99, + 50, + 49, + 87, + 81, + 88, + 69, + 88, + 67, + 75, + 77, + 74, + 45, + 107, + 86, + 71, + 55, + 112, + 80, + 82, + 81, + 76, + 121, + 88, + 112 + ], + "vid_common": [ + 123, + 34, + 112, + 111, + 108, + 121, + 95, + 99, + 111, + 109, + 109, + 105, + 116, + 115, + 34, + 58, + 34, + 70, + 73, + 69, + 76, + 68, + 126, + 65, + 81, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 65, + 68, + 51, + 115, + 68, + 111, + 108, + 109, + 118, + 55, + 79, + 111, + 101, + 89, + 55, + 78, + 113, + 121, + 48, + 104, + 117, + 115, + 66, + 65, + 90, + 121, + 45, + 113, + 81, + 68, + 100, + 65, + 100, + 112, + 122, + 68, + 106, + 66, + 73, + 78, + 53, + 48, + 65, + 106, + 104, + 48, + 34, + 44, + 34, + 97, + 108, + 108, + 95, + 101, + 118, + 97, + 108, + 115, + 95, + 100, + 105, + 103, + 101, + 115, + 116, + 34, + 58, + 34, + 70, + 73, + 69, + 76, + 68, + 126, + 76, + 120, + 97, + 78, + 98, + 114, + 104, + 107, + 119, + 117, + 121, + 51, + 122, + 54, + 122, + 83, + 66, + 111, + 97, + 71, + 75, + 55, + 114, + 53, + 99, + 50, + 86, + 120, + 57, + 85, + 120, + 121, + 50, + 75, + 111, + 80, + 48, + 77, + 54, + 101, + 121, + 121, + 79, + 101, + 34, + 44, + 34, + 112, + 97, + 121, + 108, + 111, + 97, + 100, + 95, + 98, + 121, + 116, + 101, + 95, + 108, + 101, + 110, + 34, + 58, + 53, + 51, + 44, + 34, + 110, + 117, + 109, + 95, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 110, + 111, + 100, + 101, + 115, + 34, + 58, + 52, + 44, + 34, + 109, + 117, + 108, + 116, + 105, + 112, + 108, + 105, + 99, + 105, + 116, + 121, + 34, + 58, + 49, + 125 + ], + "namespace": 3911702764, + "ns_table": [ + 2, + 0, + 0, + 0, + 36, + 45, + 0, + 179, + 31, + 0, + 0, + 0, + 236, + 216, + 39, + 233, + 53, + 0, + 0, + 0 + ], + "tx_commit": [ + 102, + 101, + 48, + 53, + 48, + 53, + 97, + 51, + 54, + 98, + 101, + 57, + 97, + 100, + 49, + 100, + 56, + 53, + 48, + 57, + 102, + 55, + 101, + 100, + 50, + 56, + 102, + 52, + 101, + 97, + 98, + 100, + 57, + 54, + 100, + 50, + 99, + 51, + 99, + 101, + 50, + 52, + 97, + 100, + 99, + 100, + 97, + 99, + 97, + 102, + 48, + 100, + 49, + 57, + 52, + 97, + 97, + 52, + 49, + 99, + 49, + 102, + 97, + 99 + ] +} \ No newline at end of file diff --git a/espressocrypto/native.go b/espressocrypto/native.go index 312265eb0c..ced28f1739 100644 --- a/espressocrypto/native.go +++ b/espressocrypto/native.go @@ -1,11 +1,68 @@ -//go:build !wasm -// +build !wasm - package espressocrypto -func verifyNamespace(namespace uint64, proof []byte, block_comm []byte, ns_table []byte, tx_comm []byte, common_data []byte) { +/* +#cgo LDFLAGS: -L${SRCDIR}/../target/lib/ -lespresso_crypto_helper +#include +#include + +bool verify_merkle_proof_helper( + const uint8_t* proof_ptr, size_t proof_len, + const uint8_t* header_ptr, size_t header_len, + const uint8_t* block_comm_ptr, size_t block_comm_len, + const uint8_t* circuit_block_ptr, size_t circuit_block_len +); +bool verify_namespace_helper( + uint64_t namespace, + const uint8_t* proof_ptr, size_t proof_len, + const uint8_t* commit_ptr, size_t commit_len, + const uint8_t* ns_table_ptr, size_t ns_table_len, + const uint8_t* tx_comm_ptr, size_t tx_comm_len, + const uint8_t* common_data_ptr, size_t common_data_len +); +*/ +import "C" +import "unsafe" + +func verifyNamespace(namespace uint64, proof []byte, blockComm []byte, nsTable []byte, txComm []byte, commonData []byte) bool { + c_namespace := C.uint64_t(namespace) + + proofPtr := (*C.uint8_t)(unsafe.Pointer(&proof[0])) + proofLen := C.size_t(len(proof)) + + blockCommPtr := (*C.uint8_t)(unsafe.Pointer(&blockComm[0])) + blockCommLen := C.size_t(len(blockComm)) + + nsTablePtr := (*C.uint8_t)(unsafe.Pointer(&nsTable[0])) + nsTableLen := C.size_t(len(nsTable)) + + txCommPtr := (*C.uint8_t)(unsafe.Pointer(&txComm[0])) + txCommLen := C.size_t(len(txComm)) + + commonDataPtr := (*C.uint8_t)(unsafe.Pointer(&commonData[0])) + commonDataLen := C.size_t(len(commonData)) + + valid_namespace_proof := bool(C.verify_namespace_helper( + c_namespace, proofPtr, proofLen, blockCommPtr, blockCommLen, nsTablePtr, nsTableLen, txCommPtr, txCommLen, commonDataPtr, commonDataLen)) + + return valid_namespace_proof } -func verifyMerkleProof(proof []byte, header []byte, block_comm []byte, circuit_comm_bytes []byte) { +func verifyMerkleProof(proof []byte, header []byte, blockComm []byte, circuitBlock []byte) bool { + + proofPtr := (*C.uint8_t)(unsafe.Pointer(&proof[0])) + proofLen := C.size_t(len(proof)) + + headerPtr := (*C.uint8_t)(unsafe.Pointer(&header[0])) + headerLen := C.size_t(len(header)) + + blockCommPtr := (*C.uint8_t)(unsafe.Pointer(&blockComm[0])) + blockCommLen := C.size_t(len(blockComm)) + + circuitBlockPtr := (*C.uint8_t)(unsafe.Pointer(&circuitBlock[0])) + circuitBlockLen := C.size_t(len(circuitBlock)) + + valid_merkle_proof := bool(C.verify_merkle_proof_helper(proofPtr, proofLen, headerPtr, headerLen, blockCommPtr, blockCommLen, circuitBlockPtr, circuitBlockLen)) + + return valid_merkle_proof } diff --git a/espressocrypto/native_test.go b/espressocrypto/native_test.go new file mode 100644 index 0000000000..26149b9a40 --- /dev/null +++ b/espressocrypto/native_test.go @@ -0,0 +1,91 @@ +package espressocrypto + +import ( + "encoding/json" + "io" + "log" + "os" + "testing" +) + +type merkleProofTestData struct { + Proof json.RawMessage `json:"proof"` + Header json.RawMessage `json:"header"` + HeaderCommitment []uint8 `json:"header_commitment"` + BlockMerkleRoot string `json:"block_merkle_root"` + HotShotCommitment []uint8 `json:"hotshot_commitment"` +} + +func TestMerkleProofVerification(t *testing.T) { + file, err := os.Open("./merkle_proof_test_data.json") + if err != nil { + log.Fatalf("Failed to open file: %v", err) + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + log.Fatalf("Failed to read file") + } + + var data merkleProofTestData + + if err := json.Unmarshal(bytes, &data); err != nil { + log.Fatalf("Failed to unmarshal the test data") + } + + r := verifyMerkleProof(data.Proof, data.Header, []byte(data.BlockMerkleRoot), data.HotShotCommitment) + if !r { + log.Fatalf("Failed to verify the merkle proof") + } + + // Tamper with the correct data and see if it will return false + data.HotShotCommitment[0] = 1 + + r = verifyMerkleProof(data.Proof, data.Header, []byte(data.BlockMerkleRoot), data.HotShotCommitment) + if r { + log.Fatalf("Failed to verify the merkle proof") + } + +} + +type namespaceProofTestData struct { + NsProof []uint8 `json:"ns_proof"` + VidCommit []uint8 `json:"vid_commit"` + VidCommon []uint8 `json:"vid_common"` + Namespace uint64 `json:"namespace"` + NsTable []uint8 `json:"ns_table"` + TxCommit []uint8 `json:"tx_commit"` +} + +func TestNamespaceProofVerification(t *testing.T) { + file, err := os.Open("./namespace_proof_test_data.json") + if err != nil { + log.Fatalf("Failed to open file: %v", err) + } + defer file.Close() + + bytes, err := io.ReadAll(file) + if err != nil { + log.Fatalf("Failed to read file") + } + + var data namespaceProofTestData + + if err := json.Unmarshal(bytes, &data); err != nil { + log.Fatalf("Failed to unmarshal the test data") + } + + r := verifyNamespace(data.Namespace, data.NsProof, data.VidCommit, data.NsTable, data.TxCommit, data.VidCommon) + if !r { + log.Fatalf("Failed to verify the namespace proof") + } + + // Tamper with the correct data and see if it will return false + data.Namespace = 1 + + r = verifyNamespace(data.Namespace, data.NsProof, data.VidCommit, data.NsTable, data.TxCommit, data.VidCommon) + if r { + log.Fatalf("Failed to verify the namespace proof") + } +} diff --git a/espressocryptowasm/espressocrypto_common.go b/espressocryptowasm/espressocrypto_common.go new file mode 100644 index 0000000000..8de0b7052c --- /dev/null +++ b/espressocryptowasm/espressocrypto_common.go @@ -0,0 +1,57 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package espressocryptowasm + +import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "encoding/json" + + espressoTypes "github.com/EspressoSystems/espresso-sequencer-go/types" +) + +// TODO move to espresso-go-sequencer: https://github.com/EspressoSystems/nitro-espresso-integration/issues/88 +func hashTxns(namespace uint32, txns []espressoTypes.Bytes) string { + hasher := sha256.New() + ns_buf := make([]byte, 4) + binary.LittleEndian.PutUint32(ns_buf, namespace) + hasher.Write(ns_buf) + for _, txn := range txns { + hasher.Write(txn) + } + hashResult := hasher.Sum(nil) + return hex.EncodeToString(hashResult) +} + +func VerifyNamespace( + namespace uint64, + proof espressoTypes.NamespaceProof, + block_comm espressoTypes.TaggedBase64, + ns_table espressoTypes.NsTable, + txs []espressoTypes.Bytes, + common_data json.RawMessage, +) { + // TODO: this code will likely no longer be used in the STF soon. + // G115: integer overflow conversion uint64 -> uint32 (gosec) + // #nosec G115 + var txnComm = hashTxns(uint32(namespace), txs) + verifyNamespace( + namespace, + proof, + []byte(block_comm.String()), + ns_table.Bytes, + []byte(txnComm), + common_data, + ) +} + +func VerifyMerkleProof( + proof json.RawMessage, + header json.RawMessage, + blockComm espressoTypes.TaggedBase64, + circuit_comm_bytes espressoTypes.Commitment, +) { + verifyMerkleProof(proof, header, []byte(blockComm.String()), circuit_comm_bytes[:]) +} diff --git a/espressocryptowasm/espressocrypto_wasm.go b/espressocryptowasm/espressocrypto_wasm.go new file mode 100644 index 0000000000..425d8b1839 --- /dev/null +++ b/espressocryptowasm/espressocrypto_wasm.go @@ -0,0 +1,60 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +//go:build wasm +// +build wasm + +package espressocryptowasm + +import ( + "unsafe" + + "github.com/offchainlabs/nitro/arbutil" +) + +//go:wasmimport espressocrypto verifyNamespace +func verify_namespace( + namespace uint64, + proof_ptr unsafe.Pointer, + proof_len uint64, + block_comm_ptr unsafe.Pointer, + block_comm_len uint64, + ns_table_ptr unsafe.Pointer, + ns_table_len uint64, + tx_comm_ptr unsafe.Pointer, + tx_comm_len uint64, + vid_comm_ptr unsafe.Pointer, + vid_comm_len uint64, +) + +//go:wasmimport espressocrypto verifyMerkleProof +func verify_merkle_proof( + proof_ptr unsafe.Pointer, + proof_len uint64, + header_ptr unsafe.Pointer, + header_len uint64, + block_comm_ptr unsafe.Pointer, + block_comm_len uint64, + circuit_comm_ptr unsafe.Pointer, + circuit_comm_len uint64, +) + +func verifyNamespace(namespace uint64, proof []byte, block_comm []byte, ns_table []byte, tx_comm []byte, common_data []byte) { + verify_namespace( + namespace, + arbutil.SliceToUnsafePointer(proof), uint64(len(proof)), + arbutil.SliceToUnsafePointer(block_comm), uint64(len(block_comm)), + arbutil.SliceToUnsafePointer(ns_table), uint64(len(ns_table)), + arbutil.SliceToUnsafePointer(tx_comm), uint64(len(tx_comm)), + arbutil.SliceToUnsafePointer(common_data), uint64(len(common_data)), + ) +} + +func verifyMerkleProof(proof []byte, header []byte, block_comm []byte, circuit_comm []byte) { + verify_merkle_proof( + arbutil.SliceToUnsafePointer(proof), uint64(len(proof)), + arbutil.SliceToUnsafePointer(header), uint64(len(header)), + arbutil.SliceToUnsafePointer(block_comm), uint64(len(block_comm)), + arbutil.SliceToUnsafePointer(circuit_comm), uint64(len(circuit_comm)), + ) +} diff --git a/espressocryptowasm/native.go b/espressocryptowasm/native.go new file mode 100644 index 0000000000..d397097093 --- /dev/null +++ b/espressocryptowasm/native.go @@ -0,0 +1,11 @@ +//go:build !wasm +// +build !wasm + +package espressocryptowasm + +func verifyNamespace(namespace uint64, proof []byte, block_comm []byte, ns_table []byte, tx_comm []byte, common_data []byte) { +} + +func verifyMerkleProof(proof []byte, header []byte, block_comm []byte, circuit_comm_bytes []byte) { + +} diff --git a/flake.nix b/flake.nix index aedfb669d7..040ac022c4 100644 --- a/flake.nix +++ b/flake.nix @@ -15,7 +15,7 @@ outputs = { flake-utils, nixpkgs, foundry, rust-overlay, ... }: let - goVersion = 21; # Change this to update the whole stack + goVersion = 22; # Change this to update the whole stack overlays = [ (import rust-overlay) (final: prev: rec {