From e89180e03dd01a9e022d2b7863946344fe5fdcb0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 12 Aug 2024 13:23:50 -0400 Subject: [PATCH 01/25] Initial draft --- .gitignore | 3 +- CMakeLists.txt | 2 +- Cargo.lock | 430 ++++++++++++++++++ Cargo.toml | 3 + rust-bindings/Cargo.toml | 18 + rust-bindings/build.rs | 59 +++ rust-bindings/fuzz/.gitignore | 4 + rust-bindings/fuzz/Cargo.toml | 21 + .../fuzz/fuzz_targets/placeholder.rs | 5 + rust-bindings/src/lib.rs | 22 + rust-bindings/wrapper.cpp | 5 + rust-bindings/wrapper.h | 4 + 12 files changed, 574 insertions(+), 2 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rust-bindings/Cargo.toml create mode 100644 rust-bindings/build.rs create mode 100644 rust-bindings/fuzz/.gitignore create mode 100644 rust-bindings/fuzz/Cargo.toml create mode 100644 rust-bindings/fuzz/fuzz_targets/placeholder.rs create mode 100644 rust-bindings/src/lib.rs create mode 100644 rust-bindings/wrapper.cpp create mode 100644 rust-bindings/wrapper.h diff --git a/.gitignore b/.gitignore index 956d66bbf..160c0b854 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ build-* cmake-build* *.zip *.tar.gz -libs/ \ No newline at end of file +libs/ +target/ diff --git a/CMakeLists.txt b/CMakeLists.txt index d880dc209..8a359056a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,7 +156,7 @@ include_directories( option(BUILD_PROOF_OF_SPACE_STATICALLY "Build ProofOfSpace target statically" OFF) IF (BUILD_PROOF_OF_SPACE_STATICALLY) message("Statically build ProofOfSpace") - target_link_libraries(ProofOfSpace -static -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive) + target_link_libraries(ProofOfSpace PUBLIC -static -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive) ENDIF() FetchContent_Declare( diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..382ec84bf --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,430 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chiapos" +version = "1.1.4" +dependencies = [ + "bindgen", + "cmake", + "link-cplusplus", +] + +[[package]] +name = "chiapos-fuzz" +version = "0.0.0" +dependencies = [ + "chiapos", + "libfuzzer-sys", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[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 = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..dd59c4f93 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["./rust-bindings", './rust-bindings/fuzz'] diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml new file mode 100644 index 000000000..b31160f2d --- /dev/null +++ b/rust-bindings/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "chiapos" +version = "1.1.4" +edition = "2021" +license = "Apache-2.0" +description = "Bindings to the chiapos C++ library." +authors = ["Brandon Haggstrom "] +homepage = "https://github.com/Chia-Network/chiapos" +repository = "https://github.com/Chia-Network/chiapos" + +[dependencies] +link-cplusplus = "1.0.9" + +[build-dependencies] +bindgen = "0.69.4" +cmake = "0.1.50" + +[dev-dependencies] diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs new file mode 100644 index 000000000..112e072fe --- /dev/null +++ b/rust-bindings/build.rs @@ -0,0 +1,59 @@ +use std::env; +use std::path::PathBuf; + +use cmake::Config; + +fn main() { + println!("cargo:rerun-if-changed=wrapper.h"); + println!("cargo:rerun-if-changed=wrapper.cpp"); + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + + let dst = Config::new(manifest_dir.parent().unwrap()) + .build_target("chiapos") + .define("BUILD_PROOF_OF_SPACE_STATICALLY", "ON") + .build(); + + let blake3_include_path = dst.join("build").join("_deps").join("blake3-src").join("c"); + + println!( + "cargo:rustc-link-search=native={}", + dst.join("build") + .join("lib") + .join("static") + .to_str() + .unwrap() + ); + + println!("cargo:rustc-link-lib=static=chiapos"); + + let bindings = bindgen::Builder::default() + .header(manifest_dir.join("wrapper.h").to_str().unwrap()) + .clang_arg("-x") + .clang_arg("c++") + .clang_arg(format!( + "-I{}", + manifest_dir.join("wrapper.cpp").to_str().unwrap() + )) + .clang_arg(format!( + "-I{}", + manifest_dir + .parent() + .unwrap() + .join("lib") + .join("include") + .to_str() + .unwrap() + )) + .clang_arg(format!("-I{}", blake3_include_path.to_str().unwrap())) + .clang_arg("-std=c++14") + .allowlist_function("something") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/rust-bindings/fuzz/.gitignore b/rust-bindings/fuzz/.gitignore new file mode 100644 index 000000000..1a45eee77 --- /dev/null +++ b/rust-bindings/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/rust-bindings/fuzz/Cargo.toml b/rust-bindings/fuzz/Cargo.toml new file mode 100644 index 000000000..00d12309b --- /dev/null +++ b/rust-bindings/fuzz/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "chiapos-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.chiapos] +path = ".." + +[[bin]] +name = "placeholder" +path = "fuzz_targets/placeholder.rs" +test = false +doc = false +bench = false diff --git a/rust-bindings/fuzz/fuzz_targets/placeholder.rs b/rust-bindings/fuzz/fuzz_targets/placeholder.rs new file mode 100644 index 000000000..355b4addf --- /dev/null +++ b/rust-bindings/fuzz/fuzz_targets/placeholder.rs @@ -0,0 +1,5 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|_data: &[u8]| {}); diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs new file mode 100644 index 000000000..ba5fabf77 --- /dev/null +++ b/rust-bindings/src/lib.rs @@ -0,0 +1,22 @@ +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(non_upper_case_globals)] + +extern crate link_cplusplus; + +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_something() { + unsafe { + assert_eq!(bindings::something(), 42); + } + } +} diff --git a/rust-bindings/wrapper.cpp b/rust-bindings/wrapper.cpp new file mode 100644 index 000000000..8e2dc1776 --- /dev/null +++ b/rust-bindings/wrapper.cpp @@ -0,0 +1,5 @@ +#include "wrapper.h" + +int something() { + return 42; +} diff --git a/rust-bindings/wrapper.h b/rust-bindings/wrapper.h new file mode 100644 index 000000000..4e4abf427 --- /dev/null +++ b/rust-bindings/wrapper.h @@ -0,0 +1,4 @@ +#include "picosha2.hpp" +#include "../src/verifier.hpp" + +int something(); From 33a653e5d296159bf29d25d1ee51660423acc502 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 11:30:28 -0400 Subject: [PATCH 02/25] Static lib and cc for wrapper --- CMakeLists.txt | 6 ++++++ Cargo.lock | 5 +++-- rust-bindings/Cargo.toml | 1 + rust-bindings/build.rs | 34 ++++++++++++++++++++++------------ 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a359056a..621dd6cf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,12 @@ IF (BUILD_PROOF_OF_SPACE_STATICALLY) target_link_libraries(ProofOfSpace PUBLIC -static -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive) ENDIF() +option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos library statically" OFF) +IF (BUILD_STATIC_CHIAPOS_LIBRARY) + message("Statically build chiapos library") + add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp) +ENDIF() + FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git diff --git a/Cargo.lock b/Cargo.lock index 382ec84bf..7fa73d351 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" dependencies = [ "jobserver", "libc", @@ -76,6 +76,7 @@ name = "chiapos" version = "1.1.4" dependencies = [ "bindgen", + "cc", "cmake", "link-cplusplus", ] diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml index b31160f2d..54a3d876e 100644 --- a/rust-bindings/Cargo.toml +++ b/rust-bindings/Cargo.toml @@ -14,5 +14,6 @@ link-cplusplus = "1.0.9" [build-dependencies] bindgen = "0.69.4" cmake = "0.1.50" +cc = "1.1.10" [dev-dependencies] diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index 112e072fe..611459337 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -1,40 +1,50 @@ use std::env; use std::path::PathBuf; +use cc::Build; use cmake::Config; fn main() { println!("cargo:rerun-if-changed=wrapper.h"); println!("cargo:rerun-if-changed=wrapper.cpp"); + println!("cargo:rerun-if-changed=../CMakeLists.txt"); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let dst = Config::new(manifest_dir.parent().unwrap()) - .build_target("chiapos") - .define("BUILD_PROOF_OF_SPACE_STATICALLY", "ON") + .build_target("chiapos_static") + .define("BUILD_STATIC_CHIAPOS_LIBRARY", "ON") .build(); let blake3_include_path = dst.join("build").join("_deps").join("blake3-src").join("c"); println!( "cargo:rustc-link-search=native={}", - dst.join("build") - .join("lib") - .join("static") - .to_str() - .unwrap() + dst.join("build").to_str().unwrap() ); - println!("cargo:rustc-link-lib=static=chiapos"); + println!("cargo:rustc-link-lib=static=chiapos_static"); + + Build::new() + .cpp(true) + .file(manifest_dir.join("wrapper.cpp")) + .include( + manifest_dir + .parent() + .unwrap() + .join("lib") + .join("include") + .to_str() + .unwrap(), + ) + .include(blake3_include_path.to_str().unwrap()) + .std("c++14") + .compile("wrapper"); let bindings = bindgen::Builder::default() .header(manifest_dir.join("wrapper.h").to_str().unwrap()) .clang_arg("-x") .clang_arg("c++") - .clang_arg(format!( - "-I{}", - manifest_dir.join("wrapper.cpp").to_str().unwrap() - )) .clang_arg(format!( "-I{}", manifest_dir From 16924d8f644032881bce3f508561d447c1bcd63c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 12:31:15 -0400 Subject: [PATCH 03/25] Temp fix --- CMakeLists.txt | 3 ++- rust-bindings/build.rs | 44 ++++++++++++++++++++++++--------------- rust-bindings/src/lib.rs | 35 +++++++++++++++++++++++++++---- rust-bindings/wrapper.cpp | 18 ++++++++++++++-- rust-bindings/wrapper.h | 11 +++++++++- 5 files changed, 86 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 621dd6cf2..7c24256a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,8 @@ ENDIF() option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos library statically" OFF) IF (BUILD_STATIC_CHIAPOS_LIBRARY) message("Statically build chiapos library") - add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp) + add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp rust-bindings/wrapper.cpp) + target_include_directories(chiapos_static PUBLIC lib/include) ENDIF() FetchContent_Declare( diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index 611459337..7e5cc0ba9 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -1,7 +1,6 @@ use std::env; use std::path::PathBuf; -use cc::Build; use cmake::Config; fn main() { @@ -22,24 +21,33 @@ fn main() { "cargo:rustc-link-search=native={}", dst.join("build").to_str().unwrap() ); + println!( + "cargo:rustc-link-search=native={}", + dst.join("build") + .join("_deps") + .join("blake3-build") + .to_str() + .unwrap() + ); + println!("cargo:rustc-link-lib=static=blake3"); println!("cargo:rustc-link-lib=static=chiapos_static"); - Build::new() - .cpp(true) - .file(manifest_dir.join("wrapper.cpp")) - .include( - manifest_dir - .parent() - .unwrap() - .join("lib") - .join("include") - .to_str() - .unwrap(), - ) - .include(blake3_include_path.to_str().unwrap()) - .std("c++14") - .compile("wrapper"); + // Build::new() + // .cpp(true) + // .file(manifest_dir.join("wrapper.cpp")) + // .include( + // manifest_dir + // .parent() + // .unwrap() + // .join("lib") + // .join("include") + // .to_str() + // .unwrap(), + // ) + // .include(blake3_include_path.to_str().unwrap()) + // .std("c++14") + // .compile("wrapper"); let bindings = bindgen::Builder::default() .header(manifest_dir.join("wrapper.h").to_str().unwrap()) @@ -57,7 +65,9 @@ fn main() { )) .clang_arg(format!("-I{}", blake3_include_path.to_str().unwrap())) .clang_arg("-std=c++14") - .allowlist_function("something") + .allowlist_function("validate_proof") + .allowlist_function("delete_byte_array") + .allowlist_type("ByteArray") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index ba5fabf77..519562d30 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -9,14 +9,41 @@ mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } +pub fn validate_proof( + seed: [u8; 32], + k: u8, + challenge: [u8; 32], + proof: [u8; 32], +) -> Option> { + unsafe { + let array = bindings::validate_proof( + seed.as_ptr(), + k, + challenge.as_ptr(), + proof.as_ptr(), + proof + .len() + .try_into() + .expect("proof must be less than 2^16 bytes long"), + ); + + if array.data.is_null() { + None + } else { + let data = std::slice::from_raw_parts(array.data, array.length); + let result = data.to_vec(); + bindings::delete_byte_array(array); + Some(result) + } + } +} + #[cfg(test)] mod tests { use super::*; #[test] - fn test_something() { - unsafe { - assert_eq!(bindings::something(), 42); - } + fn test_validate_proof() { + assert_eq!(validate_proof([0; 32], 0, [0; 32], [0; 32]), Some(vec![])); } } diff --git a/rust-bindings/wrapper.cpp b/rust-bindings/wrapper.cpp index 8e2dc1776..dadb7bb12 100644 --- a/rust-bindings/wrapper.cpp +++ b/rust-bindings/wrapper.cpp @@ -1,5 +1,19 @@ #include "wrapper.h" -int something() { - return 42; +extern "C" { + ByteArray validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len) { + Verifier v; + auto quality = v.ValidateProof(seed, k, challenge, proof, proof_len); + if (quality.GetSize() == 0) { + return ByteArray { nullptr, 0 }; + } + uint8_t *quality_buf = new uint8_t[32]; + quality.ToBytes(quality_buf); + delete[] quality_buf; + return ByteArray { quality_buf, 32 }; + } + + void delete_byte_array(ByteArray array) { + delete[] array.data; + } } diff --git a/rust-bindings/wrapper.h b/rust-bindings/wrapper.h index 4e4abf427..5cc85ba4a 100644 --- a/rust-bindings/wrapper.h +++ b/rust-bindings/wrapper.h @@ -1,4 +1,13 @@ #include "picosha2.hpp" #include "../src/verifier.hpp" -int something(); +extern "C" { + typedef struct { + uint8_t* data; + size_t length; + } ByteArray; + + ByteArray validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len); + + void delete_byte_array(ByteArray array); +} From 5f0ca7bcbf3e6c56e39fbd5228b846f42ed9571c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 13:07:34 -0400 Subject: [PATCH 04/25] Remove cc --- Cargo.lock | 1 - rust-bindings/Cargo.toml | 1 - rust-bindings/build.rs | 16 ---------------- 3 files changed, 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fa73d351..389d89b04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,7 +76,6 @@ name = "chiapos" version = "1.1.4" dependencies = [ "bindgen", - "cc", "cmake", "link-cplusplus", ] diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml index 54a3d876e..b31160f2d 100644 --- a/rust-bindings/Cargo.toml +++ b/rust-bindings/Cargo.toml @@ -14,6 +14,5 @@ link-cplusplus = "1.0.9" [build-dependencies] bindgen = "0.69.4" cmake = "0.1.50" -cc = "1.1.10" [dev-dependencies] diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index 7e5cc0ba9..e344c9d4a 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -33,22 +33,6 @@ fn main() { println!("cargo:rustc-link-lib=static=blake3"); println!("cargo:rustc-link-lib=static=chiapos_static"); - // Build::new() - // .cpp(true) - // .file(manifest_dir.join("wrapper.cpp")) - // .include( - // manifest_dir - // .parent() - // .unwrap() - // .join("lib") - // .join("include") - // .to_str() - // .unwrap(), - // ) - // .include(blake3_include_path.to_str().unwrap()) - // .std("c++14") - // .compile("wrapper"); - let bindings = bindgen::Builder::default() .header(manifest_dir.join("wrapper.h").to_str().unwrap()) .clang_arg("-x") From 7c99124a8d8d60c7cf6dee16890703ab53a42070 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:40:12 -0400 Subject: [PATCH 05/25] Fix fuzz and tests --- CMakeLists.txt | 1 + rust-bindings/Cargo.toml | 2 - rust-bindings/fuzz/Cargo.toml | 4 +- .../fuzz/fuzz_targets/placeholder.rs | 5 -- .../fuzz/fuzz_targets/validate_proof.rs | 19 +++++ rust-bindings/src/lib.rs | 70 +++++++++++++++---- src/verifier.hpp | 6 ++ 7 files changed, 85 insertions(+), 22 deletions(-) delete mode 100644 rust-bindings/fuzz/fuzz_targets/placeholder.rs create mode 100644 rust-bindings/fuzz/fuzz_targets/validate_proof.rs diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c24256a4..a8b528940 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos library statically" OFF) IF (BUILD_STATIC_CHIAPOS_LIBRARY) message("Statically build chiapos library") add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp rust-bindings/wrapper.cpp) + target_link_libraries(chiapos_static PUBLIC blake3) target_include_directories(chiapos_static PUBLIC lib/include) ENDIF() diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml index b31160f2d..e85648e69 100644 --- a/rust-bindings/Cargo.toml +++ b/rust-bindings/Cargo.toml @@ -14,5 +14,3 @@ link-cplusplus = "1.0.9" [build-dependencies] bindgen = "0.69.4" cmake = "0.1.50" - -[dev-dependencies] diff --git a/rust-bindings/fuzz/Cargo.toml b/rust-bindings/fuzz/Cargo.toml index 00d12309b..0a62dde11 100644 --- a/rust-bindings/fuzz/Cargo.toml +++ b/rust-bindings/fuzz/Cargo.toml @@ -14,8 +14,8 @@ libfuzzer-sys = "0.4" path = ".." [[bin]] -name = "placeholder" -path = "fuzz_targets/placeholder.rs" +name = "validate_proof" +path = "fuzz_targets/validate_proof.rs" test = false doc = false bench = false diff --git a/rust-bindings/fuzz/fuzz_targets/placeholder.rs b/rust-bindings/fuzz/fuzz_targets/placeholder.rs deleted file mode 100644 index 355b4addf..000000000 --- a/rust-bindings/fuzz/fuzz_targets/placeholder.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; - -fuzz_target!(|_data: &[u8]| {}); diff --git a/rust-bindings/fuzz/fuzz_targets/validate_proof.rs b/rust-bindings/fuzz/fuzz_targets/validate_proof.rs new file mode 100644 index 000000000..5e560d279 --- /dev/null +++ b/rust-bindings/fuzz/fuzz_targets/validate_proof.rs @@ -0,0 +1,19 @@ +#![no_main] + +use chiapos::validate_proof; +use libfuzzer_sys::{ + arbitrary::{Arbitrary, Unstructured}, + fuzz_target, +}; + +fuzz_target!(|data: &[u8]| { + let mut u = Unstructured::new(data); + let seed = u.arbitrary().unwrap(); + let k = u.arbitrary().unwrap(); + let challenge = u.arbitrary().unwrap(); + let proof = Vec::arbitrary(&mut u).unwrap(); + let mut quality = [0; 32]; + if !validate_proof(&seed, k, &challenge, &proof, &mut quality) { + assert_eq!(quality, [0; 32]); + } +}); diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index 519562d30..a17d65c62 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -10,30 +10,32 @@ mod bindings { } pub fn validate_proof( - seed: [u8; 32], + seed: &[u8; 32], k: u8, - challenge: [u8; 32], - proof: [u8; 32], -) -> Option> { + challenge: &[u8; 32], + proof: &[u8], + quality: &mut [u8; 32], +) -> bool { + let Some(proof_len) = proof.len().try_into().ok() else { + return false; + }; + unsafe { let array = bindings::validate_proof( seed.as_ptr(), k, challenge.as_ptr(), proof.as_ptr(), - proof - .len() - .try_into() - .expect("proof must be less than 2^16 bytes long"), + proof_len, ); if array.data.is_null() { - None + false } else { let data = std::slice::from_raw_parts(array.data, array.length); - let result = data.to_vec(); + quality.copy_from_slice(data); bindings::delete_byte_array(array); - Some(result) + true } } } @@ -43,7 +45,49 @@ mod tests { use super::*; #[test] - fn test_validate_proof() { - assert_eq!(validate_proof([0; 32], 0, [0; 32], [0; 32]), Some(vec![])); + fn test_empty_proof() { + let mut quality = [0; 32]; + assert!(!validate_proof(&[0; 32], 32, &[0; 32], &[], &mut quality)); + assert_eq!(quality, [0; 32]); + } + + #[test] + fn test_min_k_size() { + let mut quality = [0; 32]; + assert!(!validate_proof(&[0; 32], 0, &[0; 32], &[0], &mut quality)); + assert_eq!(quality, [0; 32]); + } + + #[test] + fn test_max_k_size() { + let mut quality = [0; 32]; + assert!(!validate_proof(&[0; 32], 100, &[0; 32], &[0], &mut quality)); + assert_eq!(quality, [0; 32]); + } + + #[test] + fn test_wrong_proof_length() { + let mut quality = [0; 32]; + assert!(!validate_proof( + &[0; 32], + 32, + &[0; 32], + &[0; 1000], + &mut quality + )); + assert_eq!(quality, [0; 32]); + } + + #[test] + fn test_bad_proof() { + let mut quality = [0; 32]; + assert!(!validate_proof( + &[0; 32], + 32, + &[0; 32], + &[0; 32 * 8], + &mut quality + )); + assert_eq!(quality, [0; 32]); } } diff --git a/src/verifier.hpp b/src/verifier.hpp index 36ad514d1..493bed1b1 100644 --- a/src/verifier.hpp +++ b/src/verifier.hpp @@ -65,6 +65,12 @@ class Verifier { uint16_t proof_size) { LargeBits proof_bits = LargeBits(proof_bytes, proof_size, proof_size * 8); + if (k < kMinPlotSize) { + return LargeBits(); + } + if (k > kMaxPlotSize) { + return LargeBits(); + } if (k * 64 != proof_bits.GetSize()) { return LargeBits(); } From edbd8a742939d8c922b13f01e2eadb9eeb3c2a57 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:41:33 -0400 Subject: [PATCH 06/25] Copy workflow from chiavdf --- .github/workflows/rust.yml | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..abcb421e1 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,89 @@ +name: Rust bindings + +on: + push: + branches: + - main + release: + types: [published] + pull_request: + branches: + - "**" + +permissions: + id-token: write + contents: read + +jobs: + fuzz_targets: + name: Run fuzzers + runs-on: ubuntu-latest + env: + CARGO_PROFILE_RELEASE_LTO: false + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + + - name: Install cargo-fuzz + run: cargo +nightly install cargo-fuzz + + - name: Cargo fuzz + run: | + cd rust-bindings + cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=30 || exit 255" + + build_crate: + name: Build crate + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Rustfmt + run: cargo fmt -- --files-with-diff --check + + - name: Clippy + run: cargo clippy + + - name: Tests + run: cargo test && cargo test --release + + - name: Build + run: cargo build --release + + - name: Prepare for publish + run: cp -r src rust-bindings/cpp + + - name: Publish to crates.io (dry run) + # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. + # This is necessary because the `cpp` folder is not part of the crate otherwise. + run: cargo publish --dry-run -p chiavdf --allow-dirty + + - name: Upload crate artifacts + uses: actions/upload-artifact@v4 + with: + name: crate + path: ./target/package/*-*.crate + + - name: Set Env + uses: Chia-Network/actions/setjobenv@main + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to crates.io + if: env.RELEASE == 'true' + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.cargo_registry_token }} + # See comment above for why `--allow-dirty` is used. + run: | + cp -r src rust-bindings/cpp + cargo publish -p chiavdf --allow-dirty From c27b5a5b7b89e394c30e19469136de1feba25582 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:43:38 -0400 Subject: [PATCH 07/25] chiavdf => chiapos --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index abcb421e1..6adccd835 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -66,7 +66,7 @@ jobs: - name: Publish to crates.io (dry run) # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. # This is necessary because the `cpp` folder is not part of the crate otherwise. - run: cargo publish --dry-run -p chiavdf --allow-dirty + run: cargo publish --dry-run -p chiapos --allow-dirty - name: Upload crate artifacts uses: actions/upload-artifact@v4 @@ -86,4 +86,4 @@ jobs: # See comment above for why `--allow-dirty` is used. run: | cp -r src rust-bindings/cpp - cargo publish -p chiavdf --allow-dirty + cargo publish -p chiapos --allow-dirty From a6de49cfb1520c2230b19c0eff5141e28356afb1 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:44:18 -0400 Subject: [PATCH 08/25] Update version --- Cargo.lock | 2 +- rust-bindings/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 389d89b04..47f4a43ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chiapos" -version = "1.1.4" +version = "2.0.5" dependencies = [ "bindgen", "cmake", diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml index e85648e69..547f3b972 100644 --- a/rust-bindings/Cargo.toml +++ b/rust-bindings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chiapos" -version = "1.1.4" +version = "2.0.5" edition = "2021" license = "Apache-2.0" description = "Bindings to the chiapos C++ library." From 335b2ea787c9c807495e7fed5c5ebbacfcd455c4 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:50:14 -0400 Subject: [PATCH 09/25] Copy src and CMakeLists.txt --- .github/workflows/rust.yml | 9 +++++---- rust-bindings/build.rs | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6adccd835..19740a1e4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -61,7 +61,10 @@ jobs: run: cargo build --release - name: Prepare for publish - run: cp -r src rust-bindings/cpp + run: | + mkdir rust-bindings/cpp + cp -r src rust-bindings/cpp/src + cp CMakeLists.txt rust-bindings/cpp/CMakeLists.txt - name: Publish to crates.io (dry run) # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. @@ -84,6 +87,4 @@ jobs: env: CARGO_REGISTRY_TOKEN: ${{ secrets.cargo_registry_token }} # See comment above for why `--allow-dirty` is used. - run: | - cp -r src rust-bindings/cpp - cargo publish -p chiapos --allow-dirty + run: cargo publish -p chiapos --allow-dirty diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index e344c9d4a..2e050e6d9 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -10,7 +10,15 @@ fn main() { let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let dst = Config::new(manifest_dir.parent().unwrap()) + let mut cpp_dir = manifest_dir.join("cpp"); + if !cpp_dir.exists() { + cpp_dir = manifest_dir + .parent() + .expect("can't access ../") + .to_path_buf(); + } + + let dst = Config::new(cpp_dir.as_path()) .build_target("chiapos_static") .define("BUILD_STATIC_CHIAPOS_LIBRARY", "ON") .build(); @@ -39,13 +47,7 @@ fn main() { .clang_arg("c++") .clang_arg(format!( "-I{}", - manifest_dir - .parent() - .unwrap() - .join("lib") - .join("include") - .to_str() - .unwrap() + cpp_dir.join("lib").join("include").to_str().unwrap() )) .clang_arg(format!("-I{}", blake3_include_path.to_str().unwrap())) .clang_arg("-std=c++14") From 5d55492bd572eb9b86da7360f3544831eb4e3c8a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:53:36 -0400 Subject: [PATCH 10/25] Add uint128_t and lib --- .github/workflows/rust.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 19740a1e4..ee51be302 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -63,8 +63,7 @@ jobs: - name: Prepare for publish run: | mkdir rust-bindings/cpp - cp -r src rust-bindings/cpp/src - cp CMakeLists.txt rust-bindings/cpp/CMakeLists.txt + cp -r src lib uint128_t CMakeLists.txt rust-bindings/cpp - name: Publish to crates.io (dry run) # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. From e15128f0a06a8a844eebbf51efe98d0762614778 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:58:08 -0400 Subject: [PATCH 11/25] Move wrapper --- {rust-bindings => c-bindings}/wrapper.cpp | 0 {rust-bindings => c-bindings}/wrapper.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {rust-bindings => c-bindings}/wrapper.cpp (100%) rename {rust-bindings => c-bindings}/wrapper.h (100%) diff --git a/rust-bindings/wrapper.cpp b/c-bindings/wrapper.cpp similarity index 100% rename from rust-bindings/wrapper.cpp rename to c-bindings/wrapper.cpp diff --git a/rust-bindings/wrapper.h b/c-bindings/wrapper.h similarity index 100% rename from rust-bindings/wrapper.h rename to c-bindings/wrapper.h From cccfd3dafb9eab6d646d1dff56e525c6ae113da2 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 15:59:55 -0400 Subject: [PATCH 12/25] Fixup --- .github/workflows/rust.yml | 2 +- CMakeLists.txt | 2 +- rust-bindings/build.rs | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ee51be302..760a984dd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -63,7 +63,7 @@ jobs: - name: Prepare for publish run: | mkdir rust-bindings/cpp - cp -r src lib uint128_t CMakeLists.txt rust-bindings/cpp + cp -r src lib uint128_t python-bindings c-bindings CMakeLists.txt rust-bindings/cpp - name: Publish to crates.io (dry run) # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. diff --git a/CMakeLists.txt b/CMakeLists.txt index a8b528940..393e21641 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,7 +162,7 @@ ENDIF() option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos library statically" OFF) IF (BUILD_STATIC_CHIAPOS_LIBRARY) message("Statically build chiapos library") - add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp rust-bindings/wrapper.cpp) + add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp c-bindings/wrapper.cpp) target_link_libraries(chiapos_static PUBLIC blake3) target_include_directories(chiapos_static PUBLIC lib/include) ENDIF() diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index 2e050e6d9..877fe4032 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -4,8 +4,8 @@ use std::path::PathBuf; use cmake::Config; fn main() { - println!("cargo:rerun-if-changed=wrapper.h"); - println!("cargo:rerun-if-changed=wrapper.cpp"); + println!("cargo:rerun-if-changed=../c-bindings/wrapper.h"); + println!("cargo:rerun-if-changed=../c-bindings/wrapper.cpp"); println!("cargo:rerun-if-changed=../CMakeLists.txt"); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); @@ -42,7 +42,13 @@ fn main() { println!("cargo:rustc-link-lib=static=chiapos_static"); let bindings = bindgen::Builder::default() - .header(manifest_dir.join("wrapper.h").to_str().unwrap()) + .header( + cpp_dir + .join("c-bindings") + .join("wrapper.h") + .to_str() + .unwrap(), + ) .clang_arg("-x") .clang_arg("c++") .clang_arg(format!( From 86eb306b895b5cb6d769945b160bfef561507882 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 16:04:13 -0400 Subject: [PATCH 13/25] Copy in tests --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 760a984dd..1f7c5915a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -63,7 +63,7 @@ jobs: - name: Prepare for publish run: | mkdir rust-bindings/cpp - cp -r src lib uint128_t python-bindings c-bindings CMakeLists.txt rust-bindings/cpp + cp -r src lib tests uint128_t python-bindings c-bindings CMakeLists.txt rust-bindings/cpp - name: Publish to crates.io (dry run) # We use `--allow-dirty` because the `cpp` folder is copied into the working directory. From 711e3013cb509dad9512c03522c18ce320d15b80 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 13 Aug 2024 16:09:39 -0400 Subject: [PATCH 14/25] Fuzz for 10 minutes --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1f7c5915a..9ec65ab1b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,7 +30,7 @@ jobs: - name: Cargo fuzz run: | cd rust-bindings - cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=30 || exit 255" + cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=600 || exit 255" build_crate: name: Build crate From 8bec9618e51a6c41da0521757444c0a8e716c898 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 14 Aug 2024 08:25:22 -0400 Subject: [PATCH 15/25] Address comments --- CMakeLists.txt | 6 +++--- c-bindings/wrapper.cpp | 13 ++++--------- c-bindings/wrapper.h | 10 +--------- rust-bindings/build.rs | 2 -- rust-bindings/src/lib.rs | 20 ++++++-------------- 5 files changed, 14 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 393e21641..f66687fde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,10 +159,10 @@ IF (BUILD_PROOF_OF_SPACE_STATICALLY) target_link_libraries(ProofOfSpace PUBLIC -static -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive) ENDIF() -option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos library statically" OFF) +option(BUILD_STATIC_CHIAPOS_LIBRARY "Build chiapos static library (verify-only)" OFF) IF (BUILD_STATIC_CHIAPOS_LIBRARY) - message("Statically build chiapos library") - add_library(chiapos_static STATIC src/chacha8.c src/verifier.hpp c-bindings/wrapper.cpp) + message("Build chiapos static library (verify-only)") + add_library(chiapos_static STATIC src/chacha8.c c-bindings/wrapper.cpp) target_link_libraries(chiapos_static PUBLIC blake3) target_include_directories(chiapos_static PUBLIC lib/include) ENDIF() diff --git a/c-bindings/wrapper.cpp b/c-bindings/wrapper.cpp index dadb7bb12..157dc2775 100644 --- a/c-bindings/wrapper.cpp +++ b/c-bindings/wrapper.cpp @@ -1,19 +1,14 @@ #include "wrapper.h" +#include "verifier.hpp" extern "C" { - ByteArray validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len) { + bool validate_proof(uint8_t* seed, uint8_t k, uint8_t* challenge, uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf) { Verifier v; auto quality = v.ValidateProof(seed, k, challenge, proof, proof_len); if (quality.GetSize() == 0) { - return ByteArray { nullptr, 0 }; + return false; } - uint8_t *quality_buf = new uint8_t[32]; quality.ToBytes(quality_buf); - delete[] quality_buf; - return ByteArray { quality_buf, 32 }; - } - - void delete_byte_array(ByteArray array) { - delete[] array.data; + return true; } } diff --git a/c-bindings/wrapper.h b/c-bindings/wrapper.h index 5cc85ba4a..4a6c01df3 100644 --- a/c-bindings/wrapper.h +++ b/c-bindings/wrapper.h @@ -1,13 +1,5 @@ #include "picosha2.hpp" -#include "../src/verifier.hpp" extern "C" { - typedef struct { - uint8_t* data; - size_t length; - } ByteArray; - - ByteArray validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len); - - void delete_byte_array(ByteArray array); + bool validate_proof(uint8_t* seed, uint8_t k, uint8_t* challenge, uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf); } diff --git a/rust-bindings/build.rs b/rust-bindings/build.rs index 877fe4032..0bddc1ead 100644 --- a/rust-bindings/build.rs +++ b/rust-bindings/build.rs @@ -58,8 +58,6 @@ fn main() { .clang_arg(format!("-I{}", blake3_include_path.to_str().unwrap())) .clang_arg("-std=c++14") .allowlist_function("validate_proof") - .allowlist_function("delete_byte_array") - .allowlist_type("ByteArray") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index a17d65c62..291f4d441 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -21,22 +21,14 @@ pub fn validate_proof( }; unsafe { - let array = bindings::validate_proof( - seed.as_ptr(), + bindings::validate_proof( + seed.as_ptr() as *mut u8, k, - challenge.as_ptr(), - proof.as_ptr(), + challenge.as_ptr() as *mut u8, + proof.as_ptr() as *mut u8, proof_len, - ); - - if array.data.is_null() { - false - } else { - let data = std::slice::from_raw_parts(array.data, array.length); - quality.copy_from_slice(data); - bindings::delete_byte_array(array); - true - } + quality.as_mut_ptr(), + ) } } From 30a399197844355f02a121e125402412cd859e1c Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 14 Aug 2024 08:38:50 -0400 Subject: [PATCH 16/25] Update readme --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f60f35b34..08fa0e255 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ Attacks that can provide significant space savings for the final file. ./HellmanAttacks -f "plot.dat" check ``` -## Python +## Python bindindg -Finally, python bindings are provided in the python-bindings directory. +Python bindings are provided in the python-bindings directory. ### Install @@ -85,6 +85,10 @@ Testings uses pytest. Linting uses flake8 and mypy. py.test ./tests -s -v ``` +# Rust binding + +Finally, Rust bindings are provided, but only validation of proofs of space is supported, and it cannot be used to make plots or create proofs for plots. + ## ci Building The primary build process for this repository is to use GitHub Actions to build binary wheels for MacOS, Linux (x64 and aarch64), and Windows and publish From 53552569df13184760d63cc7f69a8b87ec36ad7f Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 14 Aug 2024 09:01:29 -0400 Subject: [PATCH 17/25] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08fa0e255..96354f0f6 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Attacks that can provide significant space savings for the final file. ./HellmanAttacks -f "plot.dat" check ``` -## Python bindindg +## Python binding Python bindings are provided in the python-bindings directory. From 4853507bbfd5a091dd300d9c3cb8896404c164da Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 14 Aug 2024 13:17:00 -0400 Subject: [PATCH 18/25] Remove mut --- c-bindings/wrapper.cpp | 2 +- c-bindings/wrapper.h | 2 +- rust-bindings/src/lib.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c-bindings/wrapper.cpp b/c-bindings/wrapper.cpp index 157dc2775..962e21ab5 100644 --- a/c-bindings/wrapper.cpp +++ b/c-bindings/wrapper.cpp @@ -2,7 +2,7 @@ #include "verifier.hpp" extern "C" { - bool validate_proof(uint8_t* seed, uint8_t k, uint8_t* challenge, uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf) { + bool validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf) { Verifier v; auto quality = v.ValidateProof(seed, k, challenge, proof, proof_len); if (quality.GetSize() == 0) { diff --git a/c-bindings/wrapper.h b/c-bindings/wrapper.h index 4a6c01df3..45532b401 100644 --- a/c-bindings/wrapper.h +++ b/c-bindings/wrapper.h @@ -1,5 +1,5 @@ #include "picosha2.hpp" extern "C" { - bool validate_proof(uint8_t* seed, uint8_t k, uint8_t* challenge, uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf); + bool validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf); } diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index 291f4d441..5e786ac28 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -22,10 +22,10 @@ pub fn validate_proof( unsafe { bindings::validate_proof( - seed.as_ptr() as *mut u8, + seed.as_ptr(), k, - challenge.as_ptr() as *mut u8, - proof.as_ptr() as *mut u8, + challenge.as_ptr(), + proof.as_ptr(), proof_len, quality.as_mut_ptr(), ) From 59292bdccf0d249f247b3f788e7d8f7a2df7d2bf Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 05:24:02 -0400 Subject: [PATCH 19/25] Add C++ test for k size and Rust tests for proofs --- Cargo.lock | 7 +++ rust-bindings/Cargo.toml | 3 ++ rust-bindings/src/lib.rs | 28 ++++++++++ rust-bindings/test_proofs.txt | 97 +++++++++++++++++++++++++++++++++++ tests/test.cpp | 20 ++++++++ 5 files changed, 155 insertions(+) create mode 100644 rust-bindings/test_proofs.txt diff --git a/Cargo.lock b/Cargo.lock index 47f4a43ec..96cf3225a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,7 @@ version = "2.0.5" dependencies = [ "bindgen", "cmake", + "hex", "link-cplusplus", ] @@ -130,6 +131,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.9" diff --git a/rust-bindings/Cargo.toml b/rust-bindings/Cargo.toml index 547f3b972..9496b114d 100644 --- a/rust-bindings/Cargo.toml +++ b/rust-bindings/Cargo.toml @@ -14,3 +14,6 @@ link-cplusplus = "1.0.9" [build-dependencies] bindgen = "0.69.4" cmake = "0.1.50" + +[dev-dependencies] +hex = "0.4.3" diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index 5e786ac28..f8607b436 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -36,6 +36,34 @@ pub fn validate_proof( mod tests { use super::*; + #[test] + fn test_proofs() { + /* + * Generated by printing the inputs to validate_proof in tests/test_python_bindings.py + * Specifically, the test_k_21 function was adapted to append 100 lines to the file. + */ + let proofs = include_str!("../test_proofs.txt"); + + for line in proofs.lines() { + let mut parts = line.split(", "); + let seed = hex::decode(parts.next().unwrap()).unwrap(); + let k = parts.next().unwrap().parse().unwrap(); + let challenge = hex::decode(parts.next().unwrap()).unwrap(); + let proof = hex::decode(parts.next().unwrap()).unwrap(); + let quality = hex::decode(parts.next().unwrap()).unwrap(); + + let mut actual_quality = [0; 32]; + assert!(validate_proof( + &seed.try_into().unwrap(), + k, + &challenge.try_into().unwrap(), + &proof, + &mut actual_quality + )); + assert_eq!(actual_quality.as_slice(), quality); + } + } + #[test] fn test_empty_proof() { let mut quality = [0; 32]; diff --git a/rust-bindings/test_proofs.txt b/rust-bindings/test_proofs.txt new file mode 100644 index 000000000..301250f70 --- /dev/null +++ b/rust-bindings/test_proofs.txt @@ -0,0 +1,97 @@ +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119, cc128d31890be2ef01ed47503b8559caa2014b9262651db7c43ff1544acb0de1f354e87d88b0b1c87383394d52b6fc393fbacb0224665488cd31d7c6f52d4113a7d3e3c5cc5366668dd446586bc9c66fb607ea55596ce2d14db5ff964c2f7c7ffc4f5590b1313c6ce29a2b0ef6773256d3019f0a78ace7aef2efd8c2decefc317a98bd61722839c1b68d28eb38972b4b12b5912d9024e9b8d5a4cd878bc12192b1390a7d9dd6b82a, 753087568382550fdefa85d7907efd91d7ad10a6b2e4787cf97f848f4753302e +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119, 1773e5fe605a9f7b4b4fd4849513bf1a049cafa31a7bb9bb9cf649f3f04a109a7e65b8c7601063a94322e4abf4d9379d0f37a673c12bef027d0df44a98155f0119e2ec9e07a37bddf4567b28538d674742c7b0eb0418599746a76348a8292ee7da342419cd36a4bdc853d607b9d8aac2ed095263406994eaaed67885faaff94168de032c2c3fab48c8ceb77ae5ca8fe05cedac890b5995c8df0be3b4ed0f30d6d57cdff4268fbd02, 5a1452adbb2f64866126729ff3f096d5f5602996157ac9bfd734931ca0dc84ad +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119, 7180f6b52a24bf8d35798f6ed7d1aa2ae6c5527646e86cc2225514269b10856a7f919f16aa0871a5bb37b11b7da3ec1558f20d40fafa553e14c7317b6ca6a41d96e90666f6e86dfa97d14af3cc0e84dade8ba4ca1c7b55697fac1b1ba0a4e2c8927fb4fee8a07e01a7be884b37e062d4b62a66c008f3adc16e724f3641af0402a434bbd831954b8fb4eb0b7c1278a6d3e81e70df86147edb379e54cd9b7714145e0aa1a3ce4abd07, d404956ce6b81abd8339c62782be2278f168f7539d92a4ac4b22a9cb8653c6ee +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, b40711a88c7039756fb8a73827eabe2c0fe5a0346ca7e0a104adc0fc764f528d, 33d962ec758bed751d766f0f662d6cbe4c87303637be2c46386d1ef119909e82261d961c9a1cb490926d8330446f72ab46d2891180f46cbf1d38d9d24c19d5474e0383d85a31737af9fc4f2875f5f5db74d458f3da4720259379273e909d8a94035375c3d9e94245b4835caeae903c8cdb960fde054609c4a84c37566172cee397ee0fb2f67baedeaef1f9f93f043302a32b2e11ed2958252dddbdda4997351acb5363549a147585, 9d81a0cdc78efcd663d62313d01c58229d390ca92db0898db34c41fe950bfc24 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 433ebf5bc03dffa38536673207a21281612cef5faa9bc7a4d5b9be2fdb12cf1a, 0ca779cb88f7acb1d5d23284caa63cf157aa06b9f65ec19f8706519bbc8b693eebed034fd290b5f52a2be4dea4030f7524b34ee451d4c5a3df47cd2e051b4cb489bda1090a119cccde618ccb5ada19f50b03e9717a7caa8435b0e5065c1b4bbe91565b4af103f3f2e753bc7cb5c80345ff81fc1ce2bdbb7ac38fc02fe779609030558949029587162194850f438ecb294bef11ad8079ae45ec5fc054c89d6b9ec6ade86a71e3f3eb, 467033666a422d670745e0d60b08289eb2605753dfd085220e3c3d345aca7b6c +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 433ebf5bc03dffa38536673207a21281612cef5faa9bc7a4d5b9be2fdb12cf1a, eb96ba2a6958725cf518005fe3387a13939c432831ba36a8e8c02b47e0e063eaa76af9f1a18d4b083598bcce8374aeb6a44f4aa068b8b6987541971cb818a92649662306ebf5618d4bef1829494037ccf2f7ba4c16f8b355b4680dd2656808497dad747c8d98da8dfc060aae5499cf807c1505bed5fc477e58032acdc8130901032e1e55dcf70daa7632673fbc31ac4ccdb903563f44c1010a43c85fe633e9f7a4e93641b7305dd5, 7b7be4c868e7a3838423b4317debcad1d4719fca59df4446461ea3eabc680be3 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 88185d128d9922e0e6bcd32b07b6c7f20f27968eab447a1d8d1cdf250f79f7d3, ee74624e726f08328e52f4577389872353946305dbba5cb9e758109e5f1ae6b7a22f238eb86efdb0c9328d405d76e93f0d66fbf5f5ecc8aa171f27511999134c6f3c93c95e91189d93e3790f5453fb2170dec198c289e764d06409ae478f0a5e0fe0766abf7001a694c7466b1583bfcc50cc25eb8cf830f4ca94ddfea3a1c56a80fbd2c4bc32748b53c56cf34a79a82c72c1c8560ff47629c3bf4386017e288dc582b7b048a49410, 8d736853b3b83d37d5c9bf2b878d7e8f50f37d7812686254e7a6427887bb6870 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 88185d128d9922e0e6bcd32b07b6c7f20f27968eab447a1d8d1cdf250f79f7d3, 1c8ba13f841baa01d4b1eb64265d3c54ccac574c7573065022a29ddc829d199d7ccdf5b39359c6de0b0436eeb67b5c9ff786f23ef69238d9b258fc47c4ef02837795069fb5f26ba3807ffcf4fa8a1033220802070a7c12512ee2131274bbcb2a9bdf041970b6a8d097a3b10d7180f5ba02fbcbe7f7d01476ae117b386a225683fff4db5d946b794c4c37128a2c4c9d5d655ddad996e696f36cd82c16d6f9090014b9bcc5a6e90259, bc81572f8f7387225320659055766e5b23c320d056c04fca79bf0734e1fb6493 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, b253668f6b59f1ff28522831931e4d3c5a3de533965af22e961735437c0172cb, 42881f9e1cc301c6c299f197d3e6d66028d0f3391e81df2a973a20091991f9ddbfbba78e5d11a5b21f67167164234e70941fd95a3a844c69dfb8c9037038a9e9940a7ae6677dabf2164f48b686a854ad08064e7cfe74ea0db3799a8cf3bd318db47d0a9e367be89ee76064c184f34ee8e24f929e0541855d8da36155ec71e2d4eed00ae9c5270f6a497946b7bd26b96485f98593340a0c3fc81c676530e63eb871720aa97fe31f5c, 2d7c1b420e18f238384da943b8c5c0e46abb04be580e4eb07dd16f30e472d2e6 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e084a105a7b7d599b8346d3cba5ac51b756d7e29c7ef77fd9eff31fdeae31389, 2423ba141b274444d504f65b8eca63dc9911a80e0af2cff956447931a49f376f30beef35257d514d4a1d107794b13931b8d82fea52c853870fdd84a54598e1fbe20a1f6cb161dcbd43117f89d40dd8367d2a2e3304ad3ff864c9e354ae488e0ec3f330711cee8e4356005d4861e9aa2d0493aeec267e2fdf58c86caf93f30a57c5c8a3644a7080cd1c91b3e4efc2002f9b9cceb640f44fb22ef27d1bfb7d553b0102d8b4339141b8, 21cc213a6dd61ab317782f5a394a6d615b79732a060ce8185ae2df0298efbaca +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e084a105a7b7d599b8346d3cba5ac51b756d7e29c7ef77fd9eff31fdeae31389, 66f6ab46d3579fa694c421459e2f932676fe858789f5c5976161d66982c3d612577895987059716a0f6bffc89843e19fa4803ff00e92d47a607d71879cab6a3889ebf422fe30ec88f6bbe15b0fcef660149620a8e0f6c78444660b3e6ca56b979f494364300ae57314d9b9fb6eaab1de092fc6d7c6102c9ac391062fbe56eec955d09c8d73725852c4fe06a4ae1dea24192aa9c3172a0c0817bd8294db88d8c6de6f2eb49862ab81, 52ea873319b5ea4d5a22ca891f438822f8fcc6b1defdc36486ce4094ae227f59 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 7fde8eebf388fcff667a89be60430cc6e198b1a78cb603a39cdd09885a3336e3, 22e694a0b442b4df70c1651bf5ca73fd5499b0435ad23d93df7e5cbed43ebc31199ab0419c3610940aae2662b8e5e5b151b2f8299aaeb53aee088b80fd45960fa728dd49445a81414b6bca6144036b5d8bbe04de5cd05cb9f0178f6c9dae33fd3bc5c76435039c0eed30bec14dca831976471004345faa5de187f297b286ee71eb9124ddea3baf1722de7f1eedf68d65365225fd1fc26f339cf40eff0308ebac4d5515c4b6678860, a4f363f5d6c61d7c6e3b664d74451e727c0a217d1d9d0e80a5fca5439cbaf4cb +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 5085cb99dbe1e374ccd321e5b58182d2c42cf4840c68cfab17b0618a8285e7d6, 70b22da6991f44b0a15d189089ad85adfb259466566efa8fa35dd640b0bc429946dba73053cfde4720c5134db1b6c1439eceb206fc8b89bbedf0bbf35f22708ce70accceb0ee64b861c90e5e3fba9107a4f26e88ace455d3e4c8df30cf9331698ed8dc946fd6eec1db11d0588beea44f33027b62b64dc2087f3418f21133d1d44b9174debad445d7cc2f558268e7ed38a492718185d81a270244adaa5e2f66f0662bdcaa466027c1, 819da3ef4f8c2ced5f5f4aaa84cc2b4d02ab81a9ebd532dd3fac5235a933cb8d +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 5085cb99dbe1e374ccd321e5b58182d2c42cf4840c68cfab17b0618a8285e7d6, bbc2708b421d942f069785565618795759b775e2ab8f042dd40bfae88be67516417d4f67df56394f510339b15f0b6089d7de3ed58930e35fc00f5c1a6c929880c81c482a3a2268a6c960dcf49cc8147992f3fb437adc8b571d1cb5eb2d786fb6171165502f1f3672230722b12a69672598d31fadc464d433263bc3beb39f7f2233f64475bb856590817486dd5716b85e86041a9d83bf16b0103f72250e15519af7fd09682cbd9b8f, 9d03070095fb0daaced37bfa45461a71d7841e20842ddedb53a3e62e4cda954b +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 5085cb99dbe1e374ccd321e5b58182d2c42cf4840c68cfab17b0618a8285e7d6, 6531822575ef24a9eff3ef05534da3275a02f6bc0e8151cd7b4784cc0feca46e12511e7fb3a9d2e715e3feefb3375d728157c6b80b0e7b1a3247c0d1501e2641b487b36eb544b98e609810ce67b093d78d6a5e4a9d3ca1c5165f109e4b7f690c2698d2d6198013aa77640a24ebb150c6d2ab1f47bd7efe9faa0fd37cbf164862749f5f3b24d181ec02ad20a5163c7b3a0d4643b1724a691861a0e832ae4994703aaebba7f0571140, add8196a099ab6a3daf92ae6695474964296036b19be924ac265c6b4ebfd7a3d +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 14236fe421fe9ddb8377a19b60ce71dd655704bcefc225c9287fb11127ba6569, ac9188e5ac8871738bdb5cc03a1bd9ff83b47f289b3e62e45a355fe360fd76b53944d9cf2bf3d6342a4333a5b29ed04e09b481da2bdebdca18a8bd6735d7ff2117b91d915e03b52c2d4fe46c937288c64efd9da6aa583a926ba0fa218fb45e2fa912d3bcffd73b9267bed16f34fde7f40a6fd284f277cbbe3990d4ee60d72edcf0209df726a428e5ecd1d8705e716bf743e6d8b5cc3f098488bf75681bc1c8b925f2853cc1a35a31, 7b2db7b6540d182c49b86e4ebc7c62c03bb0fd325a8281da49404654c541d20f +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, a7911cdfdc28e6a1867e359ba426081aacfa692fdeb3e978d2a68dd924fa8c3e, 313baac053f9ee9d66a4ef7647c0e8928475d48110a222c89dce5c823da99eb005c235f646a9b300961dbee0286684fbee7ee4dee0bdcbb3252046d72f27ed8d88a4a39dbafae8ce204749c5a12be986486c041e4af5cf16cadee5a73412ea6da95086c6787d28d66bd1c8c3d88ae2c39a3b8571be54fb4812e7e8e7662ce9f5b4e59bf97e00fafd3728aceb96b1067df3a323f4b18224e1333477f18b7a2ae744e5f353a1d0b603, c487f1246b1b2842dd8e0f04892e00719e3c2ad2ac2fcdcdcf977ec82b77bc53 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, a7911cdfdc28e6a1867e359ba426081aacfa692fdeb3e978d2a68dd924fa8c3e, d65045b6fd092c8c7952d5830640eede43e62be5440aecf57eab7f75aef8f5d4f085e951afda20b3fe279133a5f03c7d77e2ec35922b1deba19cac792ddb95155da1128a87c3d1d0b8a50f5fd6493e944939ad222291b9ae2f1c6371696a17f149c15cac0cd24b6e6efdc50e68cd6b0aa59eac4f3067c1aeeafd70ba25ec34c433e67dc19944a42f8ad735fd53f1e508d63cfa3a3eeb1fe95e4e4ba7cf3d6b9ff025dc23997e8e7f, bed8327837974192ceb508277ff93d53f45cebd4bacc522a71c018dbd5593688 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, a7911cdfdc28e6a1867e359ba426081aacfa692fdeb3e978d2a68dd924fa8c3e, b82f47f8d22752c18db0e45b204d56e87d9fdaf41eb6f3f16447ab159962416795b772decdd49b65f4efd1e1d4f81409cc9f2cb733067db1b09126023d791b466bb6435131729fedf8f29c9e9eef02d6eeff98e56708b7d584325eb9988c212d893edf7df4bfc22a1f7ade1baf9e88372f4d9323fffeb794d22386d9e273fa6a52d0ca243336a7783f5db98e96679ba8e1933d432d377cac2f15881045989b18670a1f9ea94ee6a6, 39a755c116c58372465372356ff291a57989ce2cc5ccc5754c0bc32dcc2d5e87 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 21548c66b44347d877e7b405a250121cda87cd494b122f0324f7df3b6cd75f86, 0f8bb8d09bea6019d3813c553f76f1206a8e6b830fa2687610936e9ca4c2bbc60c0a294f6297dc605bcc781859072455ef829d4f60acb1ad3387a6805b342538c0067ab78baa898084d82a4863c1d89e055597090b6a7a71665e0c636515312438d6ad33d00132313d82c74ad46d7ba4c7cb41e64927e4d1eb80e8b983ac769cffba1bfae26c1a400fa8672f9175385c354a386402706d29d282c72ff98c94af9bc91a568b84af9c, 7b8df15aaf20252f84dfa93b08bd694939b57e91b99bedc93bf98bdc2109c3ef +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 21548c66b44347d877e7b405a250121cda87cd494b122f0324f7df3b6cd75f86, 26d705d2d27b6fbea27a024d41a3b5de5477f05c6cffc014ab442d9ad98726de89c09d96dcd7dcf0a46ffee263ed19209aedd6542893057e0187b1dda02ecc1a77d8e59fb9b0cdc9d458f06d1c0141acc4b5efeb7f094c4346fe3baa3b3797d145adf56f5fc7a4f511de2bb93f4ed8dae070773e7e01ca4431bdfa3df5a3dd896669c710c9349350a2b6a7b31e3a0f7b0a4fa9c5e0e187cc17ab22c4d444df084e93bfb839176cf3, fe5de500a7558b2bb589465316d1f5bf9fee97eef70de4fad0dee60539f76417 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, bc2d9ddc9857e53598677dbea82a2583879e89ce6c032c77d825187bf3f1a943, 2ca8a1197fdeb85362d0d4ee0e78d83ecfe1fbb5731422ea3f1284c269561b6f1c75ff2db24691d08760251e1450e346dc9077042105d536314a671674b61a8a3d6d051a0fb8655ae3b6c88c22101d73904a722ea254d9e038c8b3488fa68356b407a7b1fdd06071e7a0fe6148e26603b87d87f760f8876c0c2ea148bea3fed62a7b639059fa18cb50b511a98cd2a395e96302050ba3bbc5f685c0698b8cb478bc6915e0489c820b, fb1e12177ff69f56fba4505e24cd3e0801439ee7cf348bbac3160ecbfe2880ad +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 58ec452ede72799baf21664edc953297b592ad9e95f2381710593ad7a32f31ef, 3751ddba6c518bb270198972426395bc6c78623e465246ed440248cf0146a48cae72e8ce6ec0f9cef13da0ec1a70b76bb43a3a0c1a9f78df097a140c04a3ac2e94cfd926296079ae0a0b3534200288a6946314bfda9882dd20c4a57a7620601f0bed1b21f6794825e50a3b1cef97a9eb3c2abf02b4d60941061a8df627e8fdf330fe5cf4790acfd25fe0494822888f851a886e7a4b5514ffe298b37aed06fcda4aa01a4609bd840f, bb2a7c199dcbdb433050fa50107799ac067913d1755e615a58ec81e6a9f66ecf +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 79a881f99a636d0ad75f16f53668982b59f8bb96c3fbb449155fe7a08dba9cb1, 684ed21637042e100de33b77226e1d26440f6609c8e036989848e571e2371709015e86b8314ce692a5005443f5da2acabe2ad0b2fa04bd645486cf57f9fa4af35f65167c2c9b9f7a5a6324428c82cf46ca13cf36f0e360357b1af38910ba65ad47c2708d9ff56210ff4fef84ef4d22a938177ca9707695168118984973f0377fc9dd041346de9bf21606ea9e589c13ffb123c6a434fabc82f1ac29ecb5cc84a26b61a4052ee8ddbc, cf14520817270ff0ef77db4d063f682059988f1d3a1f4f277d8317d4401dd43f +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 79a881f99a636d0ad75f16f53668982b59f8bb96c3fbb449155fe7a08dba9cb1, 429fc349f25c7093792bedfd5902630dfe820844f7868f8b85df13c45fa7888282896912106614898b8f9a136aa2cd8534b6cac3a594d742e604fe1a2bb0f9b6548833d8de777450b6f39ed63394b50b9914f00ef6c1e615900410c16d435a77dfc3779aac45c0c6758f5bc3e3a33e82c082f54b8799f9c9746ee58e8822d3a08f4903edbaccbb5302e39a3167e1d210aa2027b40ccc097916c39e99aff302e3f1ecd650724b5f51, a285f49eb142dcfbf28ba4bee319db43aa136bb94604ad1eb45c01983c8dbe0d +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 79a881f99a636d0ad75f16f53668982b59f8bb96c3fbb449155fe7a08dba9cb1, 8e93e9f3ad6985bffc582d5e17671c6160c8f16aa08783f68390e84fd28408f4acf7919c20cd170709cd920a432d324a2c40dd5287a55c08f39a4ff5c4913fc3884b4523d68c1d1eec82111024ccc941f6ce98818dfbf7b89ed2f33f0121c52299d1cfc8e61504e83474b813fc4c6b8d7d42c42d16ef9d6d43c63f5692043c76108567b851efb6ecf84e4c0bef99a532f8c84db1d27063e944db5fba1118aa00999adf7f16f5d8c3, 56e2893757d1d19c20a99ad53891808d432d2e17e20adc6a45daf5296fb32ad8 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 79a881f99a636d0ad75f16f53668982b59f8bb96c3fbb449155fe7a08dba9cb1, 5d7878bcb053230d5750aeed13c6da108f256946e9e653733310f1f49945ad9b8e48a42da27b589f6501a8486a02d32a414da796c79ba9b64d000eb3d2cff3386f2747908acc8f905d6d41c6c7adece89f333e41b65384ffdb7fef58581799cea73409a4e2ae6e65363d649f141b57ff46f61f3d29df41de52205b82b794a9b4ff74e95dfa0131c7dbfa2f41b57dbad460dd00b9a42b2a1afb900b7a7599ae85fbdd6cbb01f98b38, cf8e8ec40ac4bf46c3523789685e5e8255f239f5c0f9496c585aaef36935944f +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 282a163f3e6f52ab35cf838079369bce33cf5a1f151d2822846095a6d07e1ae2, 0b8beac62e2722675381e493ddaaf34868b9335871af30a99c344fc195035d20978b272424f2e9d82a23136b5535552cd2b3175ee738d3010cb7889ad0404091fbe35a326080e1933618f0e035ed1e94f39d540a8bd498dcf14e773ac05a9df39955b92210493260c9389fc4e5f7a95d5a52224431b79899e700880efcb2e98a0d1852d96232f6f7ee0dadc076d3fe01affd59874331956b928e5eda46f4d5937aa64a7e2152b87b, 18af528db8d15e5a45d6e754d39490326fe2055ddb6033250242c7399d56958e +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 282a163f3e6f52ab35cf838079369bce33cf5a1f151d2822846095a6d07e1ae2, 865031584019f18782f95bfb0a5b32ab6c53bc84a552a9295b3a012b6ce2b2e74013e1a3382c79e25458bd1759907114aeabca3541c3974634ff757d8eadb4a302e99ee2d2fe15998fb6ba30742900623f3e5ad7ab71695caf3db0c74efbe0f70bd83bcffe3f051c06c301e03743641ae36f7f029a1e11561a4b82114db815444b33bb14ccf1028b5f0b069ae0f02e34247cd367f70be1a38f9a265264a28befbfae4f4b78ab2d0e, a7d4565562a10722b9e559b12b6d2481e9e431d67854b507322f5cf681f09d1b +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c1c3c8fdf22e3a4adef796c8fdaf739d7d3b222f995891eb8f495ca71fb932a8, 3b133b281c2f992b2daa68b6d19573ae104f59647a33adcbe1c732a89c95ab1685514c2a746564b52032f36072175d2ea7d2d631115c662c1c4de52e37fdfc3be9b970b65b7a3d22b72e573f1ff6ea20daf2adb78982cd4b6b2e07b64d147950f331d5a9f76323c787630f2df98729ea1fd9931ab704e55b8b385f9aa97ee8943f5f7059acf784c829121c092ddb9d9046a1f5d5aa56ec2ccafb20284d862fe9aabbd6bf7348ef33, 44b361c994088da5b6f8e8aebc3d77240b35b9aed708b8f0dcae4848977b6201 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c1c3c8fdf22e3a4adef796c8fdaf739d7d3b222f995891eb8f495ca71fb932a8, f85146b7b513da3913c9353ca44b39b1c3ea0355ba8d9a2d774976058e8a0e73057d6edca5ac4521495dc584ef8fde878b3a85d66c6297bce31a477e772fc0b27b50856fdd46334ec04db478c9df73f7e49675216d233edc8cc94c50a6986aa24b301bdf14cc2f50cd3aa789856aa76dce0443e18f107a793a9f60baf4b76f2b7ebefb89401a47c3dac5e0574413337f2d899c7662fd6e29f91921cf5d44ba1fb6e9bda1df618de2, 9acb33f93b60bd53ac526da37acc654e34abdfb898283bb51147c826c59f0a86 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c1c3c8fdf22e3a4adef796c8fdaf739d7d3b222f995891eb8f495ca71fb932a8, d8ab38e7afb96105e61956d08ade260daf345467a1b91abc8f7c810e2c40097281e524d3aa847aa26cc33acf6691bde66df9214cd677d391503a8168f9ab08b0cfda34d59838f2e8efc21865776d4be75f0cd268e23edf3ed0f767f40be81478ea5686fb637c87a116eea7993cdf43db3a200f0887724a96519d41fd0d5b8b923d1f7b4a25106c76affe6dac410b966e455cde402e01c3d7a8644199266ff50b56974a9fd2d959ce, 360f5abcf7475bd8e0596ba57206cbca59244377020c9b50beea986ba5bdfbb6 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 688e94a51ee508a95e761294afb7a6004b432c15d9890c80ddf23bde8caa4c26, caac6fd2ff259cd9fce6c1b5e711e36d2b3a1191d87db6ec56ef64a404204c7d08c17a455c76fb9ef046bfa8117dd4381620e34f18ff7876a0d7c4fa3f260bf127207c68307a5ca7f98439bc35aadb597cba3f685dac2065e377998f5848e3e22fee555971f15e472db17211aa45861496c1490e1e3d3825857a1b9411d7d3cc9a31e7b8660077d501e4153d2ce65091f0d58eadf9f6c9d08db8e8104957de7ec2821ab06f67c4c9, b2cfe98ec9950f6da39b1a4eab3f1c9a97d20e27abc79bacce997ceb0c33641e +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c006af35e3b4ca022ab986593a148b4f196f186168ee96604eeaa7e338e66ac1, 893093b130871430891b986a874df13f44270d047c159c67601639d5d7789863da589b802ec3459c1d54a1d0f851cb94038d2f12610d0909d00fd4459fa1ffe97713cb46f46b39bc7ce2b98ffd9b6bf2df847f0b5ede673fc7b0d96598fde77da393de68499152f48dde7793c77b3f2736568ae7b2b45ca78ebf820341f064ce8fb4e8945472040c2c706b96899e79879239d368842ab77a7199496f0ff79e297e647598b20e5a8c, 1e213d891d046fa71b797ac8306f1144ca81e31e0793080130d39e6122498d3c +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 560231689713a5933b6c8ac3e6b2f243730e49e1c6b0c610c41e8e8dad26044c, 77d65ddb6a4cb09eefc53e63aef16ad02225980dc146e05cb99fa2ed030145d967bf33767c35559aa259e7ef2a054f4700a84781784d721239737bbe35f6afd0b2684de619e770f2e5c4113cadeafd2a1969fcf7762da97b023e1608c7bfd4777b31753f80760f0eb8cd3af721e6dae718f95dcad0d6c4bf248a1ed75fcede029c65070806608e139350cad450492ee64893ea04809cc74f44427c709ac62dd357bfe0cda2e6cbf4, 84e386abc51d089bfdff87e0679d6325a88e5e705851aa7fc4d603a58256b538 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 560231689713a5933b6c8ac3e6b2f243730e49e1c6b0c610c41e8e8dad26044c, d96d159b3a8b5ad4927acb9aa22023b97f96cd618a7a110726a6012b72914b864229386527e2c34fb24ae8baed3555ebd438f3c3a52510442d2f967225b7ffd541381f47debd1b22abd2dbf6d7c2db9eb95d0419cda7c309921f85cae273dd8fa08f6ea6f7c3725274583a95a3cb6d19193b0d3cefdfdea20464e976ebd92985c5958cc4b93063335eeaebcfcb404f94e1f7d7f50edbdef28dd6af43fe007f88096df55482535ed9, a7282a196ef35249c57dd26288fca5d6e3257cb30e948ecddef3b63c346ca1cf +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 83440636eff7b2ec0f78ef7b8e480a033e8aeb67e6ecb657ce9bcdfdb21aa744, a0668c4727af7cbe2adddcec75395935e0d03f39022bfea6c37917c7b2a36388de0ef4dcf5fd368d39895f82bb23ebb2f3ec6ecb25567032413a9f31af48d5969114a0e29c2eb798dc3eee86f98e71f223b0813488efc5080649a0009b3150f210c6e2c3fd670903e13056f935b66d15bff5c93b1cd07bc90b1f69dce43365bd9042a232375058030ed7d8fbaf024c35f669b669f2a9001298a21a0d17cf31aba6bc5a784bd59e6f, c9d89f916f8f764683b02081a0c6c2ad3e62721cbe3a5ab3ffc1db7eadbc6a48 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e882dd6a247f4ada1f700f1918bd23b7c730ad62af15f5cec8de976cbad09691, f33832f3ad9f7ca1110872222d60166f7286df3d7bc2812a34826ebbc3b9a5b196ce6386f8e5c8b2af007b930bff5ed9085935eace1ebbcaf8a8c62fee1b18c3cf43a3b947f3f8de7c79ed6750a3e97aee7cee3d4b8e4b8528dfe27d6e3c38604ae573478564aa6c831c502a3cbda0ef8d6004891f9121f81475cec8e87a2f9e2d4eb8eee1afed067642dbe97c9ad96a203c34b94885e159915e3dea8556a66f58d6a9c5cb21ea03, 243db019664a1765f47067e286b460d6c65b31b791440db63303914057998564 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e882dd6a247f4ada1f700f1918bd23b7c730ad62af15f5cec8de976cbad09691, fe59df433af62ffdd9b9fc3ec6d759e35969488e425e0a426fb323947c6ec1e523f6cc0870e443bc188f7181c795941d045cdd634d389c342cc8e8d0e6bb69df75782fe92383652929cde928b96d897759b9fbbf77dd8854826773249cbef33585c83847646728733707405a4a9e9761964a79d648090a6f5a3f92510edfb328ebf5e8e5aa8d5b0eda3bc0129708e4e8c207ed7eeb1882726c22619211beecaaf94a0a19a192670f, 66a759880f28be68c1d5eaa4c63dfaeafea5bde493144884e2dc9beff491c933 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e882dd6a247f4ada1f700f1918bd23b7c730ad62af15f5cec8de976cbad09691, 0fab7f5d446e5b818dc68cbe0d991cf7e7a21d712dffb5a2286138e20f5db927cd9a007a3afeff2c2950299c617e90b6d6e50a842445598afbec67dd85b91b1e603838c58edd38dc210cd67e0b497de61637c47ac5059b848c3ffe5994392a1636f6295e7799224c74c7fb1172e54bf59abd3df89f823034ce986bb1f4ea77ad6b88b3ea558da11c7fb86145d9299b2b0a1553d14764575969e323d23b2fde01bf339be005500f78, 25dc0ae14dd7435127230b1836bbd6eaef194aa7011cff13419f401c631b067a +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, b9098fe78517adc23218f8cd7d804f55c977fb22185a4d8e752ab7e82e5c1d89, ad7c3201c9a44763c45c20b14154923559026a2b05a13af49977a659c28e28b747472706bf9b4dd346d9182cbbbfe67b248ce51b35165dd6296b7a01fc38648d4bcc8da1d37b2530490135bc9dc1dccf9c3bb5011ce0efe1af0e0d11f87b622bc2d82b80f111901c3a86ac013ae5c7c3b788aa9e3b84298991bfe1c2187e421c448a4c9c2af69260c6075cd9e4b997fd7228bdbcb21adee366b4f496aff7349f0d3456f534f878a7, 5e14a709a2999dc5e73038009039ee6394ed8950d9ef82e4d612badd1ede6e3c +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c8d26116ed30f9816a05fdf061b2535145472f388d9814e96f33f08369dbcbd0, 20021d8c7a95a5713035f90cf370f8b0e6e0608fe27d78590f5d3b101fd07ce8df283bfd1d1a74cd244722ecfb2f785ae18d0618e3f7d3a415444dab6d1da632d4b140561f6347924804f19f9f08fc6ffe35b6456c7626011ac363c4b49950fb64116f655570a0ffbfb785f88a79801a815106957813d1a653efa2e11c9233deef4bfea35049b39db9c22a752dcd5982a91cf51ac6224d89667e7525a8272e76e0852c217114e3a5, 94ee8fa56bf6e639dbfb4e54e892435caef802852ce90d25b08cf72515203ad2 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c8d26116ed30f9816a05fdf061b2535145472f388d9814e96f33f08369dbcbd0, fb6f2af1f6ec40bf95054b21e36d5d0da16f32bd1fd48d08eab4c3c52139866c02a034669b082c9e649114313f27c3a1e80513db99af52a9286506e24d8e9d7abb4c6f6994d122850530332a25218e93cec761abda9b67984405f3257b738b6ebaa154f8444f8750fa5dcec418811a3b39162f879c29c118372c31623e19c8e81fd28882bf669a3fe69e7e208043f91c5891e4f8a13a4f8072d3d7f4e2ce5b7b9928c82d25f6a6a2, 12369e2b7d9a995dfe39ed139a70ae3e64bad57bc87f54a7b52c5f8b3c2314c0 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, c8d26116ed30f9816a05fdf061b2535145472f388d9814e96f33f08369dbcbd0, ff6faa7bff7c7a53f012a33492d2b353a937124939a0b685f1d7230663237cd0b69d3b560223c052526f33f7ac003980e59e0a92c787baeb49f2c9c5c6f3ee1e7af5fa3ab3c0a5a0a611bf9c072d52edc1bc410e86d6859f4f0f99e7fd65aa7da2d348ad15c389986380b21d65594d2620f90bb9e91b5311063a883770ab757f361d4e4528c51244b6fcbb87e11fb1af174997064b61cc35470ef05601844953f7f0e47703d1aa5d, d6a33bbec34ca0c0f87e625d41b917dfe5ef34b386d03a9004e2f0a4c0df355a +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ae3c8b8d99a39542f78af83dbbb42c81cd94199ec1b5f60a0801063e95842570, 32a76bbc215d739989fe7faf43478efa653292ff93bde147381e391f4d6290dacc00312597da4ca78a250960ce6cedbd6d6622fa32794d92fdb6e9d00080a24b7506e47d728ddfe3cbe7817879adc52694c81a82cfc6eaf51bfc56ce2524253054c4f33214953a1efa4fbd2d7e4a326335a66e1f288d3aa36b29cf9e346edd95013e71e2bdbd3c36b02c4a31dbf8913ad7af29dddb5939b029feac4ee842c7cbeecb7ad74e637d77, a18a5311cdb49fd9e37813b1df4f1c255490a7f444463e075bd94700acf92bb3 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ae3c8b8d99a39542f78af83dbbb42c81cd94199ec1b5f60a0801063e95842570, 1881317264825d0087dda9fb7a1a677534b80c57c477a23f339bcedb5f939e7de286032e17b966e9fd2dcbea0927cc14178d61ece4e96ad40eb09de626727deed1d4c85739682e87f8a494dddbb0b9ce14098954bf77c0c554ecee799778bd49ec472080a0b2a5b80fd12aebb02e605bf3b87de1289dde9e8d8c54010ea9b3d95183fe292bb77bfed216aa686ab665186eb4dc4434b08a381224e3e3aa9fbd7b8c0c5540789c641f, bcf83be89279555f90382224fd88ff0b3512bc3d7eff443db49dd4c235a66bfc +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ae3c8b8d99a39542f78af83dbbb42c81cd94199ec1b5f60a0801063e95842570, 40b7423297a04fa7e23bac4842878cfb0250d448ae4c8d8978cb382fd821081b06b94a2f74febde5acaa61d74fee8591a3de149a8afa4ae1eb12c1242c007791fbe35a326080e193361cba3bbeb43c4eeec8cdc572dd8d45e7a83da7f3ef8bfa14e5eb95c37ca1efd0e7b3302f3acc25de18c596b705170fcd1333f232f47c59d1021151ffc556307b8f7bc6e1e2b01bee23186eefc4cb5cfd7d5654ffe771b9a91018ef2d79e33b, edc7c0ba2e52c295e52bd812c1712b3b483cc0ef57956298d529a8f5ea4107ee +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ae3c8b8d99a39542f78af83dbbb42c81cd94199ec1b5f60a0801063e95842570, af3b83823ca98b857be02a072bfe6b2ce99978313df9ea0b894793facb956dd2cf7c612173d9e724a70e25ab5fe755bd94063bc8ee8a777f806d5415364ae73deb31a09b6b8dacf73a4238e8b8bb39162fe1c83bdb717b8fba5ba66c078cc2dcf62ffdd4d675a961e73c68075448932d693d3ac4776228cb0e55cf88988ea1f9751cb6dea281ca398e4289d5d424c924bc469713a6ab938fbf591ede59cb8d011390600379ce749b, 6168d3b508106b6f27012f95285b0cf575d7357dec50e254551402ea07b779d9 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, d596e806d34bd7d4304b9f00ec340f02fa90c4faa17fe39729cb15c76e0de77f, 8abd3787e978c9e5559aa503984ac2e022c0144f56eac0d8069d513f7d606a2bc75bb0a1bdf748dde88e1ea653329b6802d662003697f3c0567ce0e5efb5c0407e914dc86778c93e37bf2aa20f28b160a1c60ca78900316e1b0c4bb1489c1ed153857728ce855c5cc56530afda47bddc7440779d5b4f7e3eb0ee6237c5c151bd2ea865dec12bbd014cda6051674b7a2fff4b3a33ccf22bcc47ac26ec00cd1ac235dc35e667088b94, 8f0fd65c0a21c0c9f46401e262a2924b28cd9c772cf8a1354918f8068be2b5d2 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, d596e806d34bd7d4304b9f00ec340f02fa90c4faa17fe39729cb15c76e0de77f, 16868d26023fcae0de3c656ceb5369b2ee7427b64574a3d5f701b8fc2daa4774a2a1741cffc98309f219f837155175e15994fa4565ad7249d6143af3cb44155f9a0d07ad3734185ccac950906c478b041500914fc08904eeb9f9d9c37c8512d48719c305c56908555751d2ba053599d5f39c1d6555d44f746d7276f2f3b0588755fe09ec14e8fb6944001b9dea7507c70a4bcd1714d187768e99dc678fc3a15df8042dab3b8f0ade, 3abebfa08b55972bad09645668950558bdb45e2780d65b17f2b29699a4dc338b +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, d596e806d34bd7d4304b9f00ec340f02fa90c4faa17fe39729cb15c76e0de77f, 340c04994e55e66b240fda593e1c9237ff6f596033e285f05fb2967ed2db05e98c6b8326539ae85d8c47ce46cb0e7599e6fac5691ec79343633110f532902d66018bd93edc645dbdad3ab3daefcb74aef2f71d68e3b6b5da8464a707c7d96cd493b018fd71cbfe5fa06552e8c7c2de5f3ef104d29ad3208ae3816cd1c57deb74e7407661c630ffa215b24a6f21a36f8624957f16430b6b438ca5b37026cfca7912445b69786201cf, 72a6d224ae2ac111adcd738c9e4c0a9c0b63b294aec42f0709c34bc1379ad337 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, daceee899cdb68711b2b16b1708d684b193062163a2ef8ca80e5a3f002212822, e9216638c173e354c378d1804e8dbf1dfe635a8c143202e1cd097a28301a35ba42307a9919aac278fbf954e58ecb260bcd4ee2e8620d2566d9238d4f0f0c992a4109d275c04e670cb668375dd0a219f48dfe0760b64d07c14add47c0cfc48918e69d4ccd134749e8dab6a8f239e656aaca6552f0429c72c1fe482ab2111df295b17b1d1215a3d3548b1d992c45186fa50b3b0eaa94b7bdbd792f97324a9e76c932f5e23000283890, f4b8adf8ab7e02b444ca0bad51b87cd18ed7684555bbecbafec9634ada1d3f4e +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, daceee899cdb68711b2b16b1708d684b193062163a2ef8ca80e5a3f002212822, b4d2fab7ab77e08ebe2fc0a66749aa25c057ef5a1826bbf26062528e3d6ade3e49a089b6fb65f888aa3e55f9a8d7f19ce814cc8611a6fa4e24361ea16b8f207440ca11bf95e9fbd2063ade27e8a242a236becf6de83981d7d44aa2e8641a134559dbdce759f5adc74c48dc43d099e0ec6a45ad24c15012f13fcee0e39bccccae2151dc9b54ff632c5fe1eb2574b400dfabf1bf96752b3c60b1cbb5e621e63f3c4919c9ed4906595a, 4681f1e4ad849bc1df6f261ae1a31676bde98db2bb34ab6fadc6c24547af072a +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 6b0e4bfa8dc9b19ccc121b2bd0d22da7298d7a4d72b4ba1df1bbec5d56800a66, 1c9f30563c8710ad43a8f2d6c93b244fe9f04d379764e3fd5796653458c67ae2bccfb15d659944048ae2bfb42cdb242f596993afce212fa59b1bb0da4eb85459d8ee3f8bf3ba88c1816d2b5e3b71b7f3eb74e91185fd7cdf35014c7a3caa85b0c8c64d54dab976fabcdbcf2a5aec299437ed7da986ec3a1bb00ba94593af2374094879c374ec47587030443ffb0d3b9b12e2e2bba03a8f34df3fd183c3f20947e49c544440531750, 52a8054c28d026d85ff8d379cc30b6a7d771058d8086f1a5548ba9ebc9ede3f0 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 2c19fc328bfccab923ee092d9123a9ccc9fb37bbf0d2c65d2fe12b6684ff4857, 4809d3b88a018b716bf8badab2634d997c44b7181ed1c3877104fa7718010fc576a82c6e0393cf5284f84e1a1a5928a2fe963242ad738da30cf0a708df19029a08aef9dee6d621869f560b89c8d542b5b36848dfd556c9f8cc1fc86861f94e3cfcaae039b7416e898a54d6a308dd057836e8c7fa9c76a9c23bf8c6ad67c2ea8526af194b9478041bbbbbd3abbd16a2df16b03b4c21e71bdae26387b479dbf9412a32c5710cc09732, 38068097f3b20c4797e67de5e5be9097426db3adc83faa3527cd8d6f5d2093af +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, cc74c0c353fe2c007aff8dc7db556638f52260fea1dc0bce2f04002db6ad90ce, 00510583e9ab6bdddfa34c128d86189e69d7a9c9d18e24fd15baf3cea050666c8fb9fe0f828ad07dbcb4cdd37b8c5ccabe61992d52582b0ea2bbbff6970ee6bd1150b13756d8d8d8ea4496d552f8f652f27fb8b018a588ca83dac0a7c60e9f7b0522c334b6dd293276f2da0fbf8e0effd626abd2308ad4b858c577eecffbd5c89a520a4dec2133a4c7cd13ff5cec6931f0f6235a19b5b752b96fcfe067b9c9a7535e5c1db45a4aa3, ed71e996091a1edfe82716b77f302c7c08b321b34632f0614c61d72db579a348 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 1fc244af2b96d0169a177e2559af29e0484744e4b8501d1044d76c9f7b3cf307, 9890e4aa7f6ce907bcfae525cd8fbcdadd1a84f8b82bbe2188b9819ae8d916a1ed54f49d73871c062eaa2754aa5074ecbc89f8846da6ac43ea8d4be16dd0c482af9298e8528cc9269a721780a64443713d0fb72ec1113fc2298f2a5550ab93ed98b6448e4e8b72ec8d83d5644eea2d5589b95aa0f4dae1c55b26b54699674c211a4fad21a8d25906d0c1c47c2751b2864f361c2298f46b9f7f7127e9647c3f125f71eba8a59fa069, 0e6728f3ef93ec8b42902bca69523a7af158470ef0f1a8d90f072870ef37724d +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 8ef3b0d7c4476e2c0a91ccc5867740a0549313b585345c1d12587ca2ed04e9f5, 6a3e002cf80f53f91ccce9fa83b70b3ecef4060f8b29ec4803a31e5d071c7812f5a3ab4e8e58f63bb78bedb838f3a42c875d9ee135ec0a17b39cc69af5a602704e1395fe4fac03db964e3031cbd90fc88606935c5a1d0d1706b3abd326fe649db985604ad68d6c57de299596cde0aa82f385b0845eb19e51e9e413eee214624ba6db4e7d227e834dbde8553e53ae95c3e97fac531eadbe0a3c61003f2fb2ed41b1b904280bc6b63b, fe5f5dbfec9c2ce842395fd5fbca6f53d375144ec41d3786249ca087740e22ac +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 8ef3b0d7c4476e2c0a91ccc5867740a0549313b585345c1d12587ca2ed04e9f5, a4654d4041a5195d893ae45561f4f29abcad082df652d54c17180313feafa5023350918251d0543aee22f50015fd90b7515bf3c87db5703541bb5c91348178efd49aaf8951528efad72e312f5a8af9b2161e0616c5e3aa11b90171da6db65bad628d3cacdd2abc0b95f0fa8a87e437bc6012a5f874ae4601a8f1bd6f07d352439f246f918f883c60aea7e12b05665d193db12f7426ae859ded15bbbdb825ff32fb316cdd40975728, 2ea8818091d515cf9fb7504132c9edc4e1b9a9edfc83e33fea7b86fab2cb4395 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 0d21caa7d0c6f1ff692603abec4315c560792923a6cfa5729ef8dd6d8220e0a1, 56085735855b44a677ad0980d6190d7c75180465cde9e619b6230143127584284baf8bf728d6ca00fd1c85253c354b8ef3494a6efe5627443c3b0c9da1f7d630327ca0cf3c378550f697e04fc0f68a958ede2820bb06b8fcfd2f484c35f1accfaef32c7bb31baef2eb576e605c593d7d0843d11e698cedb339ff7636cf10039dd06b4379cf4885544a39cb9c4b782e86a170ada9bbf4f10f048821dd812c9309303b839c610660a0, 4acd6ff939c29d432a377f0f620b418bd5c197c5482f9ab739a0778db777d4f9 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 0cc6a4ccca98a6360d42fde83780517edbfc32b300cd3ba0e0a5453a8166697a, c93ed39c9b2c4a7f95fff0ade9eb9944b6196391242b7eb3ae6aa740b78b0afd0739280837c9eaa31eb72c5e2435162d1c0ac941f06e475bc1b9b3d39574ad5553ccc3a680251441a1fdc67a6508021a2d819ec7d325b01615abf37987a35cf4d6e5da9f997801bbf8644dd991ad1caa633006070b19c609e86534bcbdb5d67a6500b451cfe3fc6c974ae769a264ad0ddbd3322aef7bd69d126c9ed950c8502829d46b00559a84af, 675b076c54b13406fb48879624bd0fa4e0347f4236e7fa912e918659e7b709c1 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 0cc6a4ccca98a6360d42fde83780517edbfc32b300cd3ba0e0a5453a8166697a, 1fd978b1c452482c332dd87902a9c50fc872dffea4156aee729a400181048f645dcb9a8447c7e9f7d93ffa76d217d386d7499c7b6721ae46a793ed8caa3ab06ac4f777d9a26720c6c3f58831ef045f3dff3a3f32690658695401c7b0e9852983985e0f4dd1074cb9641ee52225e91b9409bc68113460a209df83f5ab8814464a6d0fb752753cfa4b8d909f41f7b2f9b61ccf0abe1eb7ac44455b2d2e8ec9edaca8a7b2c110b1faf6, 7f38694e4639e2b88ef209390cbf8e7ef3d0a21eb159027d186682b2c73ad0af +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 683babc0c420c80a762490d4d73b271876ab07f11c80553f6e9125fb82c734f0, 8af8102aa974ebf3e7151967a3f3f0adc46d1654e105a6c5d003042dd5b0c691156553ec9a513eb34a12a128dbeb1f1122a2f801878bb16a2342280896a56258eb5244bc830a7e4cd9936c4430255ba81a0225697341a4236b0825ab99dacee6418f816104c2dae8117381536b88bd4053c7ea9eb07fdfe5185930794dc6572a27ec1e94b8523e200fc41dd091aced8565b06257bc94bd9343a8d1baad2a38dbaa8be9e70d1d2df3, 101a12d0c4958214cf321e297800a844fd74d71b231e381a7886e762e9803d44 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, f8de494d620f85b1cfed002bb1ea4fbf93f7eaa4b4b8a5c7f145b3cab041c0d3, d6314193b9cf4d69a17499ed8b24cb4eeab5f48051f97cbea4f7b8a055fcfaeb8957db1b328e9549d373ffdfd3858e3462ce60ca4f4ca9333fc51502ca37ed9ff5adc02e74badb6622fd007a731a6bf3c870ccdcfda80fa3cd2cf2538b948e322edddf2aef688f9344cbf36ea8cc5e6d225ba152aa2d617079306ad05652d485c52ee2243a80046f0a54702d30ae1d1b35d1348532af1e299fb0035f740fcd57408bfbedd098fd29, b3a4e00054cf8a535525302a48598b8bd665cf7d8938930392bf405c55185526 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 75ce1f4e6c052ea6fec089042a96928fda84ea969c99b35f6894a1819226ac5e, e116b023f8a40c17e93541fff3e2801198186f5ac5a4ffc596c17a218573798da652b7cba693396a1e3c864612c1422467ab4f8df9c46877ab5aecfda9557fe8d87214c46e00ced70478a83fdeeb56b6eae17b084f9e812076f3892fa54b83e107c24167c7b28418629d9e693c8f098bda65b5aa38dea73ba3eae95b5d6b296fc2881aab7ebaa7aec4ff0de60978c2db607ca0901e74045ddcf7dc37865d71c4797a252189176841, c96d8ba206ecc18428b68234f58de130f9576af585b0d719194f9ab9638417b3 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, e696a8ec6b7f294aa56059125664931f12997987bb52fffefc209d35492adc3c, 7b6e0deab4febf3160d42c9ba026d17a0b23fb1053c45e7f88688900845434e1e97eb59396d8a89a19bba163140d8e83b683fd6c1e529921feb20c6c17bb476308d550defd1233443a1b79cfd7aa300146b0c9424644688efd323238fc5f8096da4b1d12df553d4891e5675e183e0a4fc4ac0bad92fb0f3847e9d1fe6a699217556da5d05f60c63160ac6e6b72aba778b03714bec8cf2af9edc397c95c2c144d0a1614b8406b450a, 758a59618a499ad2c4048b4770346de0b19c0157c81674da647274a58171b490 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 4082763076bdaee3a85e52eb5893169d45d057397616251e0e699e6403238e66, 2a6b54a87dd852e8ebcf02d923bfee712e34d1806be31bb7a6468647373619934acadd526aa66dd825222b7b269e8791f0577ead11a4f85b9dd8029e5998d6188908ab98403e521f825e6587d81a93365e9890d42127a32f6987bb465459d25c8f6c9d542972f246006d346d1c78e48c86ba9a0bed38687a09a6499342c4557a0cc6aa1fa67cdc952ad699bd4d265699b088d70f09a63c33cc71b158feb6bf13463391840d8c3cb1, d0eeaa06fd86692fcce64ccd14144172b3199b8c27c2b1a2dccbde0669a4b663 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 15b165510f75144be8633336a97ac62c664d5c436db297fc13fb4d3ac147d7fd, ab64704448d8687a104e21fb2d8cf50d037e803a4e7305d001446da6411134d456265b64ae04bde79062cb939e64320e18c7034ec3134ba59aca5618f86c5b94af3d84f0668f91d3a7fa6399f38b70a99a8bdbe91cb84033086e683ca227a2ab5ba9f1915c65e400c3047c5081b9c945417d3053d5b4b69a585460c7c76bc245fe3bd2d11355e856503d20c06130da156ca0052032d641e811031565975477142d8a24279dcae2ff, fa8e81cd33305148d520b2d4b3e665c52c647050e715aeab0c963d81b816395f +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, d88c86f15bbea365d658ad95a81d45367c465f7af6f7264fb077f01747ddc77d, ee7cd4d2b438ae075e2c67c3332103c495af6ea51a934f39e346ca91c5e53dab34d085f43730576240363abd22e7cf81dcb15e906118e08c2b47ad9d2d8f6449e14408058fa2d22c892d3cabfbbb2df0c800fece030c6a22c88f767da37dd928f9f5bf862543b9937d4a8b4fdecc2bf83fe2761f4af63d214c9f64edc8248c960c700874ca2fba90c3be9523003a53b7a877f542d560849de6635ee77e098ba927db8fc0262902a3, ccca0e9aec75bed302aca4a3d46b266b31511f3b8f72598ff4ad50c77ceafeec +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, d88c86f15bbea365d658ad95a81d45367c465f7af6f7264fb077f01747ddc77d, 93a8e101d0260db9a128efff1c0bbc16167e551c593b57dbf2fb071580b75e64da41296a26ae64fe17b516ebbb86bbda5cb62939f12e12d3aab335577eb19147c10b452ccd130b0004d82bdb9b2a3922a92363b335059163e090a26c1fdade414e403284ebd997a2be6c3f710f8e5be4cde48cffbea6f0b9ca8a8ff933bca0ed7bae3cb94917bce475b6836b48135967149eb556631de81447c8ccaca8d7704604d35b54cb6c208b, c48a251169c3bee715a74dcd691464db91f6409b1c9e612b32b1a8721718a0da +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 03e3c2420f5066a5fa6e36735ed8cc4f6a251046263e1a6024f009deeee3b952, 652ea824534216fa66f0437500ac72517e3ac28acbb61de4093cb83845f96a4a33a87dffe95288d3f0b30f8cdfbd058fd082c0ef27789e31712ff840437f09efa0caef43f055b03406481ee58145731aacf8d39a5ab632e94df410b21f97382723859981c0677968aae5e4b2cc238d9b35df32a1425e896f0c9e460d5a496016573d4b9dde128bca015a0d3e8a7171223c6aa8ae60dbfb2fffd1632f3b0f53788df84c02a16d9d87, 359e97ee6d63b323a2949aa2a1135003e2ff4b9ee5c6330338da5b15bfc6aa25 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 825ac1bb838d399fb1ba55a6247e2c8c7a0c3ec25898f3c94dbfb51fa6e73951, 5e260aac181ebea4adfaeccee1be64e3fcb4280a066cb4b8bb0a05f5628f14cbad8afec392969c491a849b99d8d174676b19f270abd8ef856af2eb9f5ca1ced156089bb4a76a1d91498edc862de96f178884148e35678f5d84401e0dddd5697a1b85eeb622175572e8b0f167cfc4c8505ac0fd5a4f4ff53e6f5e30f468f8da0df63ac4e130b7e101582e57d604194dd2cde676a7b5f0af549fea8355be049d160a0ef1c5710d1b0e, f0beb48c2df7abbed15ca1e8be37370e3fc5139665c04908de1f30f7a656191c +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ad286c64373644140ff927b256b8415467b5d27645ff997010b15519d022631b, c7d0f6fc7eec09779077607c0814d4ce3216db0e077b319dd1aa07814d59fc008dd683e4d25c463936efaeaccd20b200a1823daf9f3437ee82a9ed5fa167ac045cc96242730b901f9c606c6d288e274f37c88c61f9b21b652c5a788dbc6f1e26e00caf4695ae0941d923f47a74d78aa3931e6c2c9cc074bcf98c77561ac5ab0b640705f4609f3937eaa51ae2a1aa344c30b0bc25cb949ed904000fd0ba185766f10469e98388eb5d, f222dc662b5a54bf2c923041141ebca3fe1029b75d64822ba73bde52edd6ee27 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 514970ce425bbbe11b969e5cd683f4c1b7722f7f026b45006c53a8031b2fc32e, fb934bd37301bb98ad3657b5ada703a7f752f5866a3bc724f5283d18d4ba0be3d31c50cb238df7a1c22c494c9268b84af8d5ec434f6b3dbbbae75a1c4383754ebe77101e0079a64fa9dc7ea8c41fd470b5f4409af7ffef49823f69da2ccb8d378aaa0264e49d4c88799bd7dfa7b2cfae2347f00e2804185dcfe10c8ca592b81645bf188e4344b7281d4950f136213c1931cb4db612c5d00f3651dedff6f280e543d748849c23516f, 602828f315ab3a744fbbcb2c0ea5f6b035c086700d98c4bc685d8da570f18df9 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 514970ce425bbbe11b969e5cd683f4c1b7722f7f026b45006c53a8031b2fc32e, a86ec4e5f43643ade4aa7423dae5d302d8ac800572016b4a90339d2f29f9d82ac4e202632f373a306a4c64e797bb3738eff15baade12d5e56165e15e3a122ec8ab50a4c09346f7419b904cb69f6c9b95f426fa53335f03173eb032b59762a99b56eed91f7cfdbaa05147f869f5777d9e3de701bff864c8d9ecfd05eaf7a55ebbfd0fb0c48594254497f38820b0c1a128625548bb3b49d1695f5a8806b202d41ab2e7c3a4d4e2390b, 76df871691ddf232bdcf8a5a3108c63a606fa2a2b7d5d51f324fada5a47e9f61 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 719b774d7d3b2ebd5e2ce1cde4f59a2ebb8f1f7069daa0baecc564a83abd5bc6, a7dd0c7379bcb628525e44cd26f496ed719b4fc4221480ff13a6284ee6eb40a8ae187de77ac26de8e52911a126361f8d9f201bab9f23331baecfb636c4ed87bccd2454f2184f4adc531584945159a9d5a5af2dc8789635741091508cbe190df5b7f972594c8b8f75d711ebb9498718ca3261860ba0ddf440bb0865827eea283df9be1d2b6d7adb6b2938a797a324abc1e03cc41cfc7a1649038698a59725bdbef67d8807571fee01, 51adf885d718c6ae9d63444e2acebfde7f7f424f8a4bfb20e16fb9505530c791 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 719b774d7d3b2ebd5e2ce1cde4f59a2ebb8f1f7069daa0baecc564a83abd5bc6, bfee380909dcd01d87c5e463ad092cc5a12b027ce03c1346c8705f20a2dc01b783eb5f45579458b01ec9ef3bb995cf86b98c64aee4b9d9019df5be4f90340abc8da8221d0f7500836bf4c59d7fc9a32a73a93383dfd4e389a46ac4760bfd9c5b9991220fd7cd427256f0b2486860ca22c5ba93a02aa04a3fe350b793da1580d6b733e190a69da861441bf9c2d605112c34294c98794158a0365efbe1a9aee1230e493c0f67920265, 8185ba998ac9319b84f8b083232a6e41e0d8d7a96bb596af4b6f79700aa9c36d +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 3f08f60bf2a0754139d918d80882ef46cd0257bd2f62ef5d8a1e28175c4360cc, dbb5e5277015b4a702d0b79c2e64c8571755dc52a49564715ea6edf2ea96e0b05429889b3b2a0ad6f176dead5b28dc5718e6c975c2e090b9be09afa3d250812aa66df988c1e8c3a617fc9ed8cca6beddbee1e2ea1ab388d72e286be4df9d0b29ca66fc74ccb649fb41fc43aaccda2e62018cbe75ebf2b52b9e4bb2792fe42a10faa4698fa8428262e7953d231cdc99f8d4dcabd75a0985d8315c1f626a01da539db9f0e8b5c9752c, d7296d67cf465b71689d7a416d743b498f3e899546c24d9d92b6dbcfc91af223 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 5d6941b983cd61008a67cd3865fe78cc6a4f0966f2c3fb27e1a3ec6667a545dd, a4871d716bd6c73158b815ecc59d46efb35264e67b6d45ccdc4316e324e348ff321602a89e6f6aa32fed1268379534db3800dcf31becd336af210aa07a2935feee9e4e3592f159193854f05afc58f8b837830fbf2152e11a0583d76a7b1629b2f40d79de9ae0ff7f6369cd3c914cb786a471fdfbe6a7927db20f0c843463c19702dffb6045224a9f820ef8a40439e8885d9f6aef6383787e3aec685ca8768964bb30beaa374ea138, 750d7da5cda9a8b2807700fd59234f03185508f1d25f826c32a5dcfaf026bcf1 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, dbf8f061b38993c8d7dc1f521c3df09f31359d1fb947e19efac98362f99d46de, 449bf22e0fb69ac16336d1f239b009ce157315d8a211f34945903afaf3f9527e22b6b26bce41986c602d7384f36c23c64c9154087b5fca070d5ca599c4126f964a14960236ae5eb2ccda35d80053e734e789f621d108dd568ea8df775a02c189d012be9f9145e53211cba9ed89ea241ae36f7f00bfc78e00af52b5cd8602ef0e52b988fcaede9965fe7abe82e29cfcb7f1421fa8bc13a549b15b3bfe2084a22d40ab425cb0b6bf28, 36cefc476ca8918ee934b7209937d533c1c89aa2e0a4a05401ac2bb3f4f5acc8 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 0a62d0f80072eafa5d94b92a01043d978e9ec6a473f4d4c883db99221a78fe2f, 36c392336ca3c6bf80086bce1a452b405adce1cd7053449413da0a9015a02c4292edb3297d5102abe96783a8dd48cfc23b8843cc028c205e4d6a0cf4f0b80c8413def4b1b25b5cbc13923e5d2c8f0e311a64bc2459af2de18b1aa6f1c1d6b24cc94c9a694b249a4f2e47ab39229c56ad1d1afe8c908b647a7b5dac43410e0256715ff3b78b8070a208546e497af1bf35e2b03a8d8b3d2796a70135d9d8dcf6f370b0e6efd8f6f89b, 4861ed468645c19b7f2a887a5ed17af4956d3c1f642ab15e7ac379d461e2778e +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 0a62d0f80072eafa5d94b92a01043d978e9ec6a473f4d4c883db99221a78fe2f, cd4350e179f54f67e804a7a1ae527f5b54acbdc25ae114275cda6a37986044aa38386c21a33cda20981e1a7d05d65c81d635af0ec601af17cd6152f831899df2ee7e7387668ccee56f0b2d1ca9552378a9f04d31a3669b3fb935ad6537162224b3097bb12dd878779bf614fb6d0db2c733d61cb3491f1de8dc7fbd5db64ce144e12fea42bd7b3a6bcc8f512c5846d61b5774769f0f05b9b13ee10b23668b6ccf971596264a63a134, 8d9ec53b6cf1562dc54a5bb429b08948a044364cf2c2ce115a77d1cc12419111 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, b310919be4d5c7892739294545b85928746fa6b90e568f04af5fb6c48379c3a9, 31a511be264f8304128044d75b49d23628a7c70596fea4ad3bd75329192468b523f7826a6522a116cbe3fc089d03d618c0afc1b139a0a3d184aa4b514c19741497e8ead1cc899ef34b68740e9f267ea6cb81cf29f6a9d55310970a7582314e32e1fa78c5bcec12fe8ed626918120a2b57ce1e9fafd45ea1cda096b70246712e708b6bd05c3e7ca6ca3fb40f12431c9ae7414d0dfbf3d4b82b71e2ae001d79e2ce4fb0c310880e455, dab0de1fa6fb22bff419c1d161b0ada97a25291b8fe8c73d604fb4006c64ec49 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 16ab79353cdbc2bef69a106f68d5f6b5adc70bce130df5a523212826ef117d3f, f1ee03b92b837b0abda4453f163b35c6a71b8df35b59ee4528a0357478f628482dca78d5b6595ae55d74152f60e5e176c73f5edb35f5ada326df10cc42ba597805fc6d6b0cee8103a6753aa1bb97eef8e52ab0e4bea7ebb9b13bd4cb88619659652eca9bb9d7a740abd2509ddafde6d720513a32ca463629cd650b93917f012b4968955c9d0481934a940c1f5a5ff154c8d8a0d77b605db3403897adc0a11241efd8ffd295e3cf07, 5ffb7cd28f22ede10ea3ba1e3d337f6c21caec654bac71f4545a651450b4a083 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 16ab79353cdbc2bef69a106f68d5f6b5adc70bce130df5a523212826ef117d3f, 75506ea668ba649cf7b629b5f8ccdc61ad46d711067067e6e7c879ece97a75945aa7f49a13c70d6ba54f8369335ada1c48a3686341aed896237a2ac0d9d3d8e422720b66ece12afc63ef47032ad46abcb2ff997646e67be826ef4d6d7f536966054658e05a08d0fc961b2729b186b43c2eaf79622f12f2489f01332a590828a956da1f12088135efb8b1afaabcfcd7f1f6ec40e6b032217f55ad09b5b50c68b9a5433e55b76592fc, ee77e3277451b56cc1d7be4a1dafad7219c63631a0e91f7ac7af919e665abab3 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 16ab79353cdbc2bef69a106f68d5f6b5adc70bce130df5a523212826ef117d3f, df0348290d826fbb010c0638289896148c15ff9e880ab38bb635111bbe999e71c239bc8f2c3ee9aa1038cb0f700e0045daa66853be2a4da4b81f710235d8cedbc0fa27549e61def9ed22000c0dc2629a1bc7844e0fc8784d8b18c1972c1062ed93e02d024bf91193bfc0863f5e8579064986c93696fc23595e1acf04fb52cf384eb29b7df4aac7fd568bfaf65b5b6baff19678c7cc2982007950cf152c3d32b86bd176a5e31ee7d0, 5848b13d26191e3a63d6b0c67fe61c78ecd27b9401c14d19ca58d0b4637fb64f +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 16ab79353cdbc2bef69a106f68d5f6b5adc70bce130df5a523212826ef117d3f, 7ed9bf28b7825253c26851798fffa68c950fcd5e74b2e73f86a87afc8b688fc5d2fc577510eb796349851c31d921b9cba64717165f518386efcdca4c8f9b6699efe45af65a960b8bb186550f9fe7902722e59c626fa102e3e301b3c0cf6acb2f7f95c8a9d5bed564cb76bcb3c9744f3ed8964d006b5f10dbb36e7c1e86d99d3689d40a8dda6a814a1f27199d1bba6c7325a6cd27ad96904a6698148b4613e437c6ac64a1ea6ac73e, e566d74d7609180ea3c2402f848863cf3887dae1eae2d2427550bbfa43a533d8 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, acdcbc039caf8f6b1cbc24d2f87a03785d8f60ed23be607d17950d62ea8c33f5, db9406b792c6df8cbc9427c17ee43965344ef657fa15bf322290151c32e03008b009d091640a578e669a05c8eca4c8e345a07659f2bab34eaeb61ea8cadea41ba4096bd734041990c2f1b2fcd4d5fa93802e07caf1c6e02c4a2fb8b7fc3a6d527e9d9e00602c19186ec1ceb886b8c470a4aec333bf787569ab810a9d4bace55582de31d041cd07aff29da126f076d3809cc63ea3af39ab7d4655188eb9c1640ebf2343c9b054f344, 3f0ce1a687bfecf03ceefbca354e3be638138b15233b2a71539421a3023bc4b7 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, dfe4224c822504739c7efea90fd28bf76a31a68ecb99096166eab251f5535a17, cccedd6b65af562fa15265ab9d52905ac004d7002f6d0cedcb78cd37534e0e6695438076f01ee63728a309e333bf30130bce47ea60d0fe09a63d579bda711e557c04eb8618e27ba6addbb153c2fb0ed6ca9157cca8486a02d32a414da796c79ba9b64d000eb3d2cff3d6319028d1700e7e26b7432757627acbbc2f133a34ad2f2d307d04b065bfa0e7b9e2505b82fb9621eac5fdcb3f32e331813c9699054d3db5f86e225e85bcd2, 98f8b9179b090139639a20fe25e9d4c113613f5ba1d735a187c449bd6a8e57c2 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, eb23c65a4244616063b70d7502b960e5d5d07b51a02472ab8a01e7482da34e47, 608c0c6488770ef50722e87345ba822409b2013a04777d5f236faa9dedf1eb5cbb149566a9d81d7f46485a4d102a57670177d9b56c179e81beac0c675f55794889b6e0b80013348253823066006f2dd62cb70e4b1f425f5be19b0cd5a4c93088920eba49e5e7e8c9ef290938e8cfc8be3a8d0688125ed5f02e26ad8b77123e8f95339cd1848956ee4ef091e4327bb7b85b1e90c814d1d44d5683c86e09ad0863e4cd82a156fa32d4, 8f237fe86d5a97b4e5757569910965be4dc41dca078e45a49e88c0b3c28be6a8 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 702a137706cb2ef04842c238c2b37fed632682838b99b0a6f504cf6b0188dafd, c8099f854f750f61b40059de36f7649312fee111e920601ba60f498cebecb658f66e1b52bf37847cdb9132a1d250bbe7b8c69997bf7b71345c7baa15d83a9cf2df6081a4eb75a4367ab85694b4029ff222fdc173643c464f833e71c739691eaaddae7ff2e1b7db86339a45015c36d89f34ffcb5ded230b19502f5353d804e2130b459fff248bea3851f763681b7f3ea1ce8569b4c52013c4611a4554a75fd81a8e8bd4a883f2e147, e97b035e7c5a5a4fa25b00981720e37881cdc9dcf98d43dab86301796fe93628 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 702a137706cb2ef04842c238c2b37fed632682838b99b0a6f504cf6b0188dafd, bc286471c8543054eb80bafe90300d0bb76251bde46882a575a1af20a7a1975baafda64b4e2edbd89454b98e838f2b9a91d12b72366dcb359606648b0ac74f0765ab6eace6fa0a9d811f4bafa846f1aa20976a8ea50ed55b5cffcd47de15edc4521df0daf574de612960413a57c057aef856e5a36ba648c5fb132c18ad9908c612c093c7655a89c12722e68ddeadff751afb4c40cc3c9cb8c6e9b0dc28ff395cca9b44837c9401eb, ed02ae42c098cfd5e61be4515a5fc4c41c1b8af1e7c48e82830edf9d26cf4154 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 43b728ce5e336e2e48a38a61805a73c094e7c1e6bbd6f85bedd679986aea5bfb, d1bb6cff791a4e88fd5090c65cec2782b8d04653804615ee3d4fa41eabfc3309aa40775aeed40219defdad26ef9e4617877e6cdae37478aa0593c9b91f12024153045391f8cf0aeb05299a844422b519581dfd9c5ebe5fa583554a11656ea34a22d627715fbd403cbedabfc6c695d815a761f25c85e954dde1ac4032bac41d6d96ae472eb853aec6f5036669a346835918aa3150108cbf8c24f58a303aecf0387f77f9b7febf854b, b22e8b39d4b2732ac84cab80bbdb0b8209cf96de8c480799352be9111748a09b +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, 43b728ce5e336e2e48a38a61805a73c094e7c1e6bbd6f85bedd679986aea5bfb, 47de7baf9fb58fd200b776254b2cc989f409738900d29f6fc4da23c099bfdfc99aa33330df4b7d553caad2051d8e3c596844d320149ae2afa4ddc392478045619a771955aa1f07213b63030faf6da30bbe13dd24039657f92659cb695c5c66197c7ef3b260b9dbbc2967e15363fe4b7928bdb35ad697a2dd4e6e1b27900142d9ed82d9147a16fb953eda7c4a4dba81b64b1fff291af62f80225ba9b2ca1e1a8518c61d0a2718e319, fd76e34f8478c3dd7d8fce94795edb454208b44acebb4508f0319a2203a7f640 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, ca3076acbb6ec2b695ebf174caa57ef3f8421707948a72029cc3836b9572c9d7, aab0a61fdebf7f8dbce407161c83ea2fe2c2e452ab8e9baec8a1e137d0ec11ac9b1252003bf062dbbb1336f7b52b3a4983a6effebc0f4f2a8273f571ef1e88eb29a96a04bc056a9afd08b4e8478a3bdae6bdfaeafcfb42d27d6dc85cf0f7ee783b3b626ca0c309f871db325cd6e83281f51b051e4f11b59392b4d1ee66b8e1a1875baa86c8bf320c3c1d31f4a5fb5aa82605bbf6f35128ab665f9a8d3ee0f4381e4ba204aa92f22a, e20833bee7e32117c5035f3520867773a1cdd467842e426673b4a1f29b628d48 +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, a40fb80ad4287819ecda5efac01c74c78d7cb00ca5f9eb5f6c0f19bd09936ac1, 82007a9843d76382e47fe53480c812a59726b238879b174fa099b5243eea2341741027d49e0e1fc21baf4e59d7948f8742da92c570f6e5c7826165b625be858fb535bda4443f9e2a176c3a73086df99754a06b58d5eb54161b613b0d9f44c89dfe0d4c0789ab6c71f77924652e22e4a1b165ccec456d5bff4393a195af1b19720fb24d43cf64d82ae22972beb81a981d21704743881424644cc4867fda3dd9454b14f7f053cda29d, db767485b317a74084e19cd3868ad0ca699f6e32123a84b2ce8f90e28f8e76fe +05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17, 21, a40fb80ad4287819ecda5efac01c74c78d7cb00ca5f9eb5f6c0f19bd09936ac1, c41bf8607b26054fae8b32bac33b8ea1ed038357c59252d73a75c3290646bf86c9c55059039f45e13f07258b2fff906492e50363f20448368addc7f1831a58e6c0df9fd7a3cf4e66ee9edb60989d96e2c59f520b8bead607434faebb582996b3cccaf65543576535e88d373f5533faf0b610d41e9411ca2cc335ba5328b330d2bc81fe35d91a0da5704b04048803f57b0beff44171a16d78a9230efe55e98c35484723d764645d6d, 2f9d00c2a19938d5528c0529a1dd2841abee7e15d04436eb715791904c3cf2d9 diff --git a/tests/test.cpp b/tests/test.cpp index a64057ed2..58b2fbfde 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -591,6 +591,26 @@ TEST_CASE("(De)Serialization") } } +TEST_CASE("K Sizes") +{ + Verifier verifier = Verifier(); + uint8_t challenge[32]; + + SECTION("Minimum K Size") + { + uint8_t proof_data[0]; + LargeBits result = verifier.ValidateProof(plot_id_1, kMinPlotSize - 1, challenge, proof_data, 0); + REQUIRE(result.GetSize() == 0); + } + + SECTION("Maximum K Size") + { + uint8_t proof_data[200 * 8]; + LargeBits result = verifier.ValidateProof(plot_id_1, 200, challenge, proof_data, 200 * 8); + REQUIRE(result.GetSize() == 0); + } +} + void HexToBytes(const string& hex, uint8_t* result) { for (unsigned int i = 0; i < hex.length(); i += 2) { From 8e40bf360f4c5a5a51319de4797c837ae37198ae Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 05:30:27 -0400 Subject: [PATCH 20/25] Fix array initialization --- tests/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test.cpp b/tests/test.cpp index 58b2fbfde..707180b3b 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -598,16 +598,18 @@ TEST_CASE("K Sizes") SECTION("Minimum K Size") { - uint8_t proof_data[0]; + uint8_t *proof_data = new uint8_t[0]; LargeBits result = verifier.ValidateProof(plot_id_1, kMinPlotSize - 1, challenge, proof_data, 0); REQUIRE(result.GetSize() == 0); + delete[] proof_data; } SECTION("Maximum K Size") { - uint8_t proof_data[200 * 8]; + uint8_t *proof_data = new uint8_t[200 * 8]; LargeBits result = verifier.ValidateProof(plot_id_1, 200, challenge, proof_data, 200 * 8); REQUIRE(result.GetSize() == 0); + delete[] proof_data; } } From 52f60b23a8a20649226c0351b7ef7d81691f5d9e Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 05:43:09 -0400 Subject: [PATCH 21/25] Add test generation script and extend proof test in Rust --- rust-bindings/src/lib.rs | 70 ++++++++++++++++++++++++++++--- tools/generate_rust_proof_test.py | 44 +++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tools/generate_rust_proof_test.py diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index f8607b436..9259bce7a 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -34,6 +34,8 @@ pub fn validate_proof( #[cfg(test)] mod tests { + use std::{fs, path::PathBuf}; + use super::*; #[test] @@ -42,25 +44,83 @@ mod tests { * Generated by printing the inputs to validate_proof in tests/test_python_bindings.py * Specifically, the test_k_21 function was adapted to append 100 lines to the file. */ - let proofs = include_str!("../test_proofs.txt"); + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_proofs.txt"); + let proofs = fs::read_to_string(path).unwrap(); for line in proofs.lines() { let mut parts = line.split(", "); - let seed = hex::decode(parts.next().unwrap()).unwrap(); + let seed = hex::decode(parts.next().unwrap()) + .unwrap() + .try_into() + .unwrap(); let k = parts.next().unwrap().parse().unwrap(); - let challenge = hex::decode(parts.next().unwrap()).unwrap(); + let challenge = hex::decode(parts.next().unwrap()) + .unwrap() + .try_into() + .unwrap(); let proof = hex::decode(parts.next().unwrap()).unwrap(); let quality = hex::decode(parts.next().unwrap()).unwrap(); + // The test should pass with the initial data. let mut actual_quality = [0; 32]; assert!(validate_proof( - &seed.try_into().unwrap(), + &seed, k, - &challenge.try_into().unwrap(), + &challenge, &proof, &mut actual_quality )); assert_eq!(actual_quality.as_slice(), quality); + + // If you change the seed, the test should fail. + let mut actual_quality = [0; 32]; + let mut new_seed = seed; + new_seed[0] = new_seed[0].wrapping_add(1); + assert!(!validate_proof( + &new_seed, + k, + &challenge, + &proof, + &mut actual_quality + )); + assert_eq!(actual_quality, [0; 32]); + + // If you change the K size, the test should fail. + let mut actual_quality = [0; 32]; + let new_k = k.wrapping_add(1); + assert!(!validate_proof( + &seed, + new_k, + &challenge, + &proof, + &mut actual_quality + )); + + // If you change the challenge, the test should fail. + let mut actual_quality = [0; 32]; + let mut new_challenge = challenge; + new_challenge[0] = new_challenge[0].wrapping_add(1); + assert!(!validate_proof( + &seed, + k, + &new_challenge, + &proof, + &mut actual_quality + )); + assert_eq!(actual_quality, [0; 32]); + + // If you change the proof, the test should fail. + let mut actual_quality = [0; 32]; + let mut new_proof = proof; + new_proof[0] = new_proof[0].wrapping_add(1); + assert!(!validate_proof( + &seed, + k, + &challenge, + &new_proof, + &mut actual_quality + )); + assert_eq!(actual_quality, [0; 32]); } } diff --git a/tools/generate_rust_proof_test.py b/tools/generate_rust_proof_test.py new file mode 100644 index 000000000..b400a5876 --- /dev/null +++ b/tools/generate_rust_proof_test.py @@ -0,0 +1,44 @@ +from chiapos import DiskPlotter, DiskProver, Verifier +from hashlib import sha256 +from pathlib import Path +import os + +plot_seed = bytes.fromhex( + "05683404333717545b0a6f0c0dde9710e4d3fe2d5cc6cc0a090a0b818bab0f17" +) + +pl = DiskPlotter() +pl.create_plot_disk( + ".", + ".", + ".", + "myplot.dat", + 21, + bytes([1, 2, 3, 4, 5]), + plot_seed, + 300, + 32, + 8192, + 8, + False, +) +pl = None +pr = DiskProver(str(Path("myplot.dat"))) + +path = "rust-bindings/test_proofs.txt" +os.remove(path) + +v = Verifier() +for i in range(100): + if i % 100 == 0: + print(i) + challenge = sha256(i.to_bytes(4, "big")).digest() + for index, quality in enumerate(pr.get_qualities_for_challenge(challenge)): + proof = pr.get_full_proof(challenge, index) + assert len(proof) == 8 * pr.get_size() + with open(path, "a+") as f: + f.write( + f"{plot_seed.hex()}, {pr.get_size()}, {challenge.hex()}, {proof.hex()}, {quality.hex()}\n" + ) + computed_quality = v.validate_proof(plot_seed, pr.get_size(), challenge, proof) + assert computed_quality == quality From b101f5937a2f5ea4eab4ead698574ad75237ccf6 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 05:44:39 -0400 Subject: [PATCH 22/25] Update comment --- rust-bindings/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index 9259bce7a..0fbc1d5a5 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -41,8 +41,7 @@ mod tests { #[test] fn test_proofs() { /* - * Generated by printing the inputs to validate_proof in tests/test_python_bindings.py - * Specifically, the test_k_21 function was adapted to append 100 lines to the file. + * Generated by tools/generate_rust_proof_test.py */ let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_proofs.txt"); let proofs = fs::read_to_string(path).unwrap(); From ed7875285b3c45ac79df2409883b475b7338b720 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 06:08:33 -0400 Subject: [PATCH 23/25] nullptr --- tests/test.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test.cpp b/tests/test.cpp index 707180b3b..395eb009e 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -598,10 +598,8 @@ TEST_CASE("K Sizes") SECTION("Minimum K Size") { - uint8_t *proof_data = new uint8_t[0]; - LargeBits result = verifier.ValidateProof(plot_id_1, kMinPlotSize - 1, challenge, proof_data, 0); + LargeBits result = verifier.ValidateProof(plot_id_1, kMinPlotSize - 1, challenge, nullptr, 0); REQUIRE(result.GetSize() == 0); - delete[] proof_data; } SECTION("Maximum K Size") From 3e983e9a2e3c2112474764f3d2d17ff3543c1b37 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 11:57:45 -0400 Subject: [PATCH 24/25] Address comments part 1 --- c-bindings/wrapper.cpp | 6 +++--- c-bindings/wrapper.h | 2 +- rust-bindings/src/lib.rs | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/c-bindings/wrapper.cpp b/c-bindings/wrapper.cpp index 962e21ab5..072391793 100644 --- a/c-bindings/wrapper.cpp +++ b/c-bindings/wrapper.cpp @@ -2,10 +2,10 @@ #include "verifier.hpp" extern "C" { - bool validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf) { + bool validate_proof(const uint8_t* plot_id, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf) { Verifier v; - auto quality = v.ValidateProof(seed, k, challenge, proof, proof_len); - if (quality.GetSize() == 0) { + auto quality = v.ValidateProof(plot_id, k, challenge, proof, proof_len); + if (quality.GetSize() != 256) { return false; } quality.ToBytes(quality_buf); diff --git a/c-bindings/wrapper.h b/c-bindings/wrapper.h index 45532b401..a6236c36f 100644 --- a/c-bindings/wrapper.h +++ b/c-bindings/wrapper.h @@ -1,5 +1,5 @@ #include "picosha2.hpp" extern "C" { - bool validate_proof(const uint8_t* seed, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf); + bool validate_proof(const uint8_t* plot_id, uint8_t k, const uint8_t* challenge, const uint8_t* proof, uint16_t proof_len, uint8_t* quality_buf); } diff --git a/rust-bindings/src/lib.rs b/rust-bindings/src/lib.rs index 0fbc1d5a5..66eb5b1ae 100644 --- a/rust-bindings/src/lib.rs +++ b/rust-bindings/src/lib.rs @@ -10,7 +10,7 @@ mod bindings { } pub fn validate_proof( - seed: &[u8; 32], + plot_id: &[u8; 32], k: u8, challenge: &[u8; 32], proof: &[u8], @@ -22,7 +22,7 @@ pub fn validate_proof( unsafe { bindings::validate_proof( - seed.as_ptr(), + plot_id.as_ptr(), k, challenge.as_ptr(), proof.as_ptr(), @@ -48,7 +48,7 @@ mod tests { for line in proofs.lines() { let mut parts = line.split(", "); - let seed = hex::decode(parts.next().unwrap()) + let plot_id = hex::decode(parts.next().unwrap()) .unwrap() .try_into() .unwrap(); @@ -63,7 +63,7 @@ mod tests { // The test should pass with the initial data. let mut actual_quality = [0; 32]; assert!(validate_proof( - &seed, + &plot_id, k, &challenge, &proof, @@ -73,10 +73,10 @@ mod tests { // If you change the seed, the test should fail. let mut actual_quality = [0; 32]; - let mut new_seed = seed; - new_seed[0] = new_seed[0].wrapping_add(1); + let mut new_plot_id = plot_id; + new_plot_id[0] = new_plot_id[0].wrapping_add(1); assert!(!validate_proof( - &new_seed, + &new_plot_id, k, &challenge, &proof, @@ -88,7 +88,7 @@ mod tests { let mut actual_quality = [0; 32]; let new_k = k.wrapping_add(1); assert!(!validate_proof( - &seed, + &plot_id, new_k, &challenge, &proof, @@ -100,7 +100,7 @@ mod tests { let mut new_challenge = challenge; new_challenge[0] = new_challenge[0].wrapping_add(1); assert!(!validate_proof( - &seed, + &plot_id, k, &new_challenge, &proof, @@ -113,7 +113,7 @@ mod tests { let mut new_proof = proof; new_proof[0] = new_proof[0].wrapping_add(1); assert!(!validate_proof( - &seed, + &plot_id, k, &challenge, &new_proof, From e81f6f5a49bd45df3ea52b76a4ee85ec0c9133ed Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 15 Aug 2024 12:41:03 -0400 Subject: [PATCH 25/25] Remove print --- tools/generate_rust_proof_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/generate_rust_proof_test.py b/tools/generate_rust_proof_test.py index b400a5876..1d7e46a5a 100644 --- a/tools/generate_rust_proof_test.py +++ b/tools/generate_rust_proof_test.py @@ -30,8 +30,6 @@ v = Verifier() for i in range(100): - if i % 100 == 0: - print(i) challenge = sha256(i.to_bytes(4, "big")).digest() for index, quality in enumerate(pr.get_qualities_for_challenge(challenge)): proof = pr.get_full_proof(challenge, index)