From ab123381af7559f748e8edd8c08038c257686dd4 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 16 Sep 2024 12:24:21 -0400 Subject: [PATCH 001/233] remove kdf as dep --- Cargo.toml | 2 -- src/http/client.rs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d6d0e7f..8c1bfc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,7 @@ base64 = "0.21.2" url = { version = "2.2.2", features = ["serde"] } derive_more = "0.99.11" rustc-hex = "2" -mm2_net = { path = "../../mm2_net" } http = "0.2.12" -common = { path = "../../common" } async-trait = "0.1.76" thiserror = "1.0.40" percent-encoding = "2.1.0" diff --git a/src/http/client.rs b/src/http/client.rs index 04c910e..30d1fb4 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -191,8 +191,6 @@ impl EndpointSchema { #[cfg(all(target_arch = "wasm32", test))] mod wasm_tests { use super::*; - use common::log::info; - use common::log::wasm_log::register_wasm_log; use once_cell::sync::Lazy; use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; @@ -204,8 +202,6 @@ mod wasm_tests { password: "password".to_string(), }); - fn init_test_env() { register_wasm_log(); } - #[wasm_bindgen_test] async fn test_dispatcher_invalid_base_url() { let bad_conf = ClientConf { @@ -220,7 +216,6 @@ mod wasm_tests { async fn test_sia_wasm_client_wip() { use crate::http::endpoints::TxpoolBroadcastRequest; use crate::transaction::V2Transaction; - init_test_env(); let client = SiaApiClient::new(CONF.clone()).await.unwrap(); let tx_str = r#" From a7be9b5d3dd72473a04da2021e7d77ed7eee27cf Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 17 Sep 2024 19:51:46 -0400 Subject: [PATCH 002/233] use rustls for reqwest on linux target --- Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 8c1bfc6..8baf7e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,10 @@ web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMo [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = { version = "0.3.2" } + +# TODO; choose between rustls-tls or native-tls for linux target +# for linux target, this requires an additional dep, `libssl-dev`(debian) +# either install libssl-dev in all CI's or use rustls-tls +# All other targets use the system's default ssl implementation +[target.x86_64-unknown-linux-gnu.dependencies] +reqwest = { version = "0.11.9", features = ["json", "rustls-tls"] } \ No newline at end of file From d8f96285e050e23d2ae4d6634a806e32e0b4dc9a Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 17 Sep 2024 21:53:41 -0400 Subject: [PATCH 003/233] align dep versions to kdf --- Cargo.lock | 1357 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 961 insertions(+), 396 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c2d8cc..a4a0a07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,27 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -34,36 +13,38 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.5.2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "async-trait" +version = "0.1.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "backtrace" -version = "0.3.59" +name = "base64" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64" @@ -77,11 +58,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitvec" -version = "0.19.6" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" dependencies = [ "funty", "radium", @@ -91,9 +78,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "0.5.11" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ "arrayref", "arrayvec", @@ -111,30 +98,33 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" -version = "1.5.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.7.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.1.15" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" -dependencies = [ - "shlex", -] +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" @@ -144,36 +134,51 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", + "num-integer", "num-traits", "serde", + "time", "wasm-bindgen", - "windows-targets 0.52.6", + "winapi", ] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] [[package]] -name = "convert_case" -version = "0.4.0" +name = "console_error_panic_hook" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -181,24 +186,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] [[package]] name = "curve25519-dalek" -version = "3.2.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest", @@ -207,6 +212,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cxx" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b49af8e551e84f85d6def97c42b8d93bc5bb0817cce96b56a472b1b19b5bfc2" +dependencies = [ + "cc", + "codespan-reporting", + "lazy_static", + "proc-macro2", + "quote", + "scratch", + "syn 1.0.95", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.95", +] + [[package]] name = "darling" version = "0.13.4" @@ -228,7 +277,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 1.0.95", ] [[package]] @@ -239,20 +288,18 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 1.0.95", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", - "syn 2.0.76", + "syn 1.0.95", ] [[package]] @@ -266,9 +313,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.5.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ "serde", "signature", @@ -291,11 +338,11 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -304,18 +351,64 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ + "matches", "percent-encoding", ] @@ -325,43 +418,93 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -376,26 +519,33 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] -name = "gimli" -version = "0.24.0" +name = "getrandom" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] [[package]] name = "h2" -version = "0.3.26" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -403,7 +553,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -412,15 +562,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "hashbrown" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hex" @@ -441,9 +591,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -452,21 +602,21 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -486,27 +636,54 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "winapi", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ - "cc", + "cxx", + "cxx-build", ] [[package]] @@ -517,45 +694,81 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ + "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" -version = "2.4.0" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.1", +] + +[[package]] +name = "indexmap" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lexical-core" version = "0.7.6" @@ -563,63 +776,91 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", - "bitflags", - "cfg-if", + "bitflags 1.3.2", + "cfg-if 1.0.0", "ryu", "static_assertions", ] [[package]] name = "libc" -version = "0.2.158" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "log" -version = "0.4.22" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "matches" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.17" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] -name = "miniz_oxide" -version = "0.4.4" +name = "mio" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ - "adler", - "autocfg", + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", ] [[package]] -name = "mio" -version = "1.0.2" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "hermit-abi", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] name = "nom" -version = "6.2.2" +version = "6.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a7a9657c84d5814c6196b68bb4429df09c18b1573806259fba397ea4ad0d44" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" dependencies = [ "bitvec", "funty", @@ -629,43 +870,91 @@ dependencies = [ ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "num-integer" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ "autocfg", + "num-traits", ] [[package]] -name = "object" -version = "0.24.0" +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" -version = "0.3.1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -673,29 +962,32 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -712,7 +1004,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.14", "libc", "rand_chacha", "rand_core", @@ -735,7 +1027,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.14", ] [[package]] @@ -747,13 +1039,22 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "reqwest" -version = "0.11.27" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" dependencies = [ - "base64", + "base64 0.13.0", "bytes", "encoding_rs", "futures-core", @@ -762,32 +1063,46 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", + "lazy_static", "log", "mime", - "once_cell", + "native-tls", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", "tokio", - "tower-service", + "tokio-native-tls", + "tokio-rustls", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] [[package]] name = "rustc-hex" @@ -796,64 +1111,148 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustls" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" dependencies = [ - "semver", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.0", ] [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] -name = "semver" -version = "1.0.23" +name = "scratch" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] [[package]] name = "serde" -version = "1.0.209" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", - "memchr", "ryu", "serde", ] @@ -889,7 +1288,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.95", ] [[package]] @@ -899,62 +1298,76 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", "opaque-debug", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "sia-rust" version = "0.1.0" dependencies = [ - "base64", + "async-trait", + "base64 0.21.7", "blake2b_simd", "chrono", "derive_more", "ed25519-dalek", + "futures", + "getrandom 0.2.9", "hex", + "http", + "js-sys", "nom", + "once_cell", + "percent-encoding", "reqwest", "rustc-hex", "serde", + "serde-wasm-bindgen", "serde_json", "serde_with", + "thiserror", + "tokio", "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", ] [[package]] name = "signature" -version = "1.6.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.5.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", - "windows-sys 0.52.0", + "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "static_assertions" version = "1.1.0" @@ -969,15 +1382,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.6.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.109" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", @@ -986,9 +1399,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -996,163 +1409,264 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.95", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "system-configuration" -version = "0.5.1" +name = "tempfile" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", + "cfg-if 1.0.0", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "termcolor" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ - "core-foundation-sys", - "libc", + "winapi-util", ] [[package]] -name = "tap" -version = "1.0.1" +name = "thiserror" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.39.3" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ - "backtrace", + "autocfg", "bytes", "libc", "mio", "pin-project-lite", "socket2", - "windows-sys 0.52.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +dependencies = [ + "rustls", + "tokio", + "webpki", ] [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", + "tracing", ] [[package]] name = "tower-service" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if 1.0.0", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "typenum" -version = "1.17.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" -version = "2.5.2" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", "idna", + "matches", "percent-encoding", "serde", ] +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ + "log", "try-lock", ] @@ -1170,37 +1684,36 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ - "cfg-if", - "once_cell", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -1208,9 +1721,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1218,189 +1731,261 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0dfda4d3b3f8acbc3c291b09208081c203af457fb14a229783b06e2f128aa7" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c2e18093f11c19ca4e188c177fecc7c372304c311189f12c2f9bea5b7324ac7" +dependencies = [ + "proc-macro2", + "quote", +] [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "windows-core" -version = "0.52.0" +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "windows-targets 0.52.6", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.42.1", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[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" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" -version = "0.50.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "winapi", ] [[package]] @@ -1409,43 +1994,23 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.76", -] - [[package]] name = "zeroize" -version = "1.3.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 1.0.95", + "synstructure", ] From 0bce6ab7ea20691bd623a81880ffcd255e295442 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 08:43:33 -0400 Subject: [PATCH 004/233] remove debugging code --- src/http/client/wasm/wasm_fetch.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/http/client/wasm/wasm_fetch.rs b/src/http/client/wasm/wasm_fetch.rs index 39139f2..9e6b33a 100644 --- a/src/http/client/wasm/wasm_fetch.rs +++ b/src/http/client/wasm/wasm_fetch.rs @@ -223,8 +223,7 @@ impl FetchRequest { let future = JsFuture::from(request_promise); let resp_value = future.await.map_err(|e| FetchError::Transport { uri: uri.clone(), - //error: stringify_js_error(&e), - error: format!("Triggers a CORS(I think) error!! {:?}", e).to_string(), + error: stringify_js_error(&e), })?; let js_response: JsResponse = match resp_value.dyn_into() { Ok(res) => res, @@ -233,8 +232,6 @@ impl FetchRequest { return Err(FetchError::Internal(error)); }, }; - let _status = StatusCode::from_u16(js_response.status()).map_err(FetchError::InvalidStatusCode)?; - let _headers = js_response.headers(); let fetch_response = FetchResponse::from_js_response(js_response).await?; Ok(fetch_response) From 3f32f886d062c482a1370782a0c9a595acc0d2c3 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 08:43:50 -0400 Subject: [PATCH 005/233] fix unit test --- src/http/client/wasm/wasm_fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/client/wasm/wasm_fetch.rs b/src/http/client/wasm/wasm_fetch.rs index 9e6b33a..0e7a5ce 100644 --- a/src/http/client/wasm/wasm_fetch.rs +++ b/src/http/client/wasm/wasm_fetch.rs @@ -297,6 +297,6 @@ mod tests { .unwrap_err(); assert!(err .to_string() - .contains("Request cannot be constructed from a URL that includes credentials")); + .contains("is an url with embedded credentials")); } } From 49779972b7383b4ec048ae26a7695e7faf69fce5 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 09:12:26 -0400 Subject: [PATCH 006/233] move wasm client tests --- src/http/client.rs | 87 +---------------------------------------- src/http/client/wasm.rs | 77 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 86 deletions(-) diff --git a/src/http/client.rs b/src/http/client.rs index 30d1fb4..cbf6a7c 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -186,89 +186,4 @@ impl EndpointSchema { Ok(url) } -} - -#[cfg(all(target_arch = "wasm32", test))] -mod wasm_tests { - use super::*; - use once_cell::sync::Lazy; - use wasm_bindgen::prelude::*; - use wasm_bindgen_test::*; - - wasm_bindgen_test_configure!(run_in_browser); - - static CONF: Lazy = Lazy::new(|| ClientConf { - url: Url::parse("https://sia-walletd.komodo.earth/").unwrap(), - password: "password".to_string(), - }); - - #[wasm_bindgen_test] - async fn test_dispatcher_invalid_base_url() { - let bad_conf = ClientConf { - url: Url::parse("http://user:password@example.com").unwrap(), - password: "password".to_string(), - }; - - let client = SiaApiClient::new(bad_conf).await.unwrap(); - } - - #[wasm_bindgen_test] - async fn test_sia_wasm_client_wip() { - use crate::http::endpoints::TxpoolBroadcastRequest; - use crate::transaction::V2Transaction; - let client = SiaApiClient::new(CONF.clone()).await.unwrap(); - - let tx_str = r#" - { - "siacoinInputs": [ - { - "parent": { - "id": "h:27248ab562cbbee260e07ccae87c74aae71c9358d7f91eee25837e2011ce36d3", - "leafIndex": 21867, - "merkleProof": [ - "h:ac2fdcbed40f103e54b0b1a37c20a865f6f1f765950bc6ac358ff3a0e769da50", - "h:b25570eb5c106619d4eef5ad62482023df7a1c7461e9559248cb82659ebab069", - "h:baa78ec23a169d4e9d7f801e5cf25926bf8c29e939e0e94ba065b43941eb0af8", - "h:239857343f2997462bed6c253806cf578d252dbbfd5b662c203e5f75d897886d", - "h:ad727ef2112dc738a72644703177f730c634a0a00e0b405bd240b0da6cdfbc1c", - "h:4cfe0579eabafa25e98d83c3b5d07ae3835ce3ea176072064ea2b3be689e99aa", - "h:736af73aa1338f3bc28d1d8d3cf4f4d0393f15c3b005670f762709b6231951fc" - ], - "siacoinOutput": { - "value": "772999980000000000000000000", - "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" - }, - "maturityHeight": 0 - }, - "satisfiedPolicy": { - "policy": { - "type": "pk", - "policy": "ed25519:968e286ef5df3954b7189c53a0b4b3d827664357ebc85d590299b199af46abad" - }, - "signatures": [ - "sig:7a2c332fef3958a0486ef5e55b70d2a68514ff46d9307a85c3c0e40b76a19eebf4371ab3dd38a668cefe94dbedff2c50cc67856fbf42dce2194b380e536c1500" - ] - } - } - ], - "siacoinOutputs": [ - { - "value": "2000000000000000000000000", - "address": "addr:1d9a926b1e14b54242375c7899a60de883c8cad0a45a49a7ca2fdb6eb52f0f01dfe678918204" - }, - { - "value": "770999970000000000000000000", - "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" - } - ], - "minerFee": "10000000000000000000" - } - "#; - let tx: V2Transaction = serde_json::from_str(tx_str).unwrap(); - let req = TxpoolBroadcastRequest { - transactions: vec![], - v2transactions: vec![tx], - }; - let resp = client.dispatcher(req).await.unwrap(); - } -} +} \ No newline at end of file diff --git a/src/http/client/wasm.rs b/src/http/client/wasm.rs index efa88d9..b24582a 100644 --- a/src/http/client/wasm.rs +++ b/src/http/client/wasm.rs @@ -117,3 +117,80 @@ impl ApiClient for Client { // unless custom implementations for the traits methods are needed #[async_trait] impl ApiClientHelpers for Client {} + +#[cfg(all(target_arch = "wasm32", test))] +mod wasm_tests { + use super::*; + use blake2b_simd::Hash; + use once_cell::sync::Lazy; + use wasm_bindgen::prelude::*; + use wasm_bindgen_test::*; + use log::info; + + wasm_bindgen_test_configure!(run_in_browser); + + static CONF: Lazy = Lazy::new(|| Conf { + server_url: Url::parse("https://sia-walletd.komodo.earth/").unwrap(), + headers: HashMap::new(), + }); + + #[wasm_bindgen_test] + async fn test_sia_wasm_client_wip() { + use crate::http::endpoints::TxpoolBroadcastRequest; + use crate::transaction::V2Transaction; + let client = Client::new(CONF.clone()).await.unwrap(); + + let tx_str = r#" + { + "siacoinInputs": [ + { + "parent": { + "id": "h:27248ab562cbbee260e07ccae87c74aae71c9358d7f91eee25837e2011ce36d3", + "leafIndex": 21867, + "merkleProof": [ + "h:ac2fdcbed40f103e54b0b1a37c20a865f6f1f765950bc6ac358ff3a0e769da50", + "h:b25570eb5c106619d4eef5ad62482023df7a1c7461e9559248cb82659ebab069", + "h:baa78ec23a169d4e9d7f801e5cf25926bf8c29e939e0e94ba065b43941eb0af8", + "h:239857343f2997462bed6c253806cf578d252dbbfd5b662c203e5f75d897886d", + "h:ad727ef2112dc738a72644703177f730c634a0a00e0b405bd240b0da6cdfbc1c", + "h:4cfe0579eabafa25e98d83c3b5d07ae3835ce3ea176072064ea2b3be689e99aa", + "h:736af73aa1338f3bc28d1d8d3cf4f4d0393f15c3b005670f762709b6231951fc" + ], + "siacoinOutput": { + "value": "772999980000000000000000000", + "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" + }, + "maturityHeight": 0 + }, + "satisfiedPolicy": { + "policy": { + "type": "pk", + "policy": "ed25519:968e286ef5df3954b7189c53a0b4b3d827664357ebc85d590299b199af46abad" + }, + "signatures": [ + "sig:7a2c332fef3958a0486ef5e55b70d2a68514ff46d9307a85c3c0e40b76a19eebf4371ab3dd38a668cefe94dbedff2c50cc67856fbf42dce2194b380e536c1500" + ] + } + } + ], + "siacoinOutputs": [ + { + "value": "2000000000000000000000000", + "address": "addr:1d9a926b1e14b54242375c7899a60de883c8cad0a45a49a7ca2fdb6eb52f0f01dfe678918204" + }, + { + "value": "770999970000000000000000000", + "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" + } + ], + "minerFee": "10000000000000000000" + } + "#; + let tx: V2Transaction = serde_json::from_str(tx_str).unwrap(); + let req = TxpoolBroadcastRequest { + transactions: vec![], + v2transactions: vec![tx], + }; + let resp = client.dispatcher(req).await.unwrap(); + } +} From 815d30b8d62c01262ec58ec0e551326f393e40a4 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 09:13:15 -0400 Subject: [PATCH 007/233] set cdylib in Cargo.toml --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8baf7e6..8c060d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ name = "sia-rust" version = "0.1.0" edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib", "rlib"] [dependencies] ed25519-dalek = { version = "1.0.1", features = ["serde"] } From d334a2f733d680ee1d2129eb3adcb737d76e463a Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 09:13:45 -0400 Subject: [PATCH 008/233] don't use tokio for wasm test env --- Cargo.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8c060d9..f624583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ serde_with = "1.14.0" nom = "6.1.2" blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } +log = { version = "0.4.19", "features" = ["std"] } hex = "0.4.2" reqwest = { version = "0.11.9", features = ["json"]} base64 = "0.21.2" @@ -24,10 +25,8 @@ http = "0.2.12" async-trait = "0.1.76" thiserror = "1.0.40" percent-encoding = "2.1.0" - [dev-dependencies] once_cell = "1.18.0" -tokio = "1.28.2" [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2.9", features = ["js"] } @@ -42,6 +41,10 @@ web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMo [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = { version = "0.3.2" } +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +tokio = "1.28.2" + + # TODO; choose between rustls-tls or native-tls for linux target # for linux target, this requires an additional dep, `libssl-dev`(debian) # either install libssl-dev in all CI's or use rustls-tls From 274e438b05044869d21450d814d19a9167314a6c Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 18 Sep 2024 15:00:44 -0400 Subject: [PATCH 009/233] fix wasm tx signing --- src/encoding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding.rs b/src/encoding.rs index 9d798f0..77689d3 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -41,7 +41,7 @@ impl Encoder { /// writes a length-prefixed []byte to the underlying stream. pub fn write_len_prefixed_bytes(&mut self, data: &[u8]) { - self.buffer.extend_from_slice(&data.len().to_le_bytes()); + self.buffer.extend_from_slice(&(data.len() as u64).to_le_bytes()); self.buffer.extend_from_slice(data); } From e4251bbb1223ea65b48df951a661a703d1836229 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 17:17:15 -0400 Subject: [PATCH 010/233] fix tokio tests --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f624583..0439eed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMo wasm-bindgen-test = { version = "0.3.2" } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = "1.28.2" +tokio = { version = "1.28.2", features = ["rt", "macros"] } # TODO; choose between rustls-tls or native-tls for linux target From 8658825ffac1fe2e11c84a0fe36acae09bf74c43 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 17:17:23 -0400 Subject: [PATCH 011/233] Cargo.lock --- Cargo.lock | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a4a0a07..103674a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,6 +1319,7 @@ dependencies = [ "hex", "http", "js-sys", + "log", "nom", "once_cell", "percent-encoding", @@ -1505,9 +1506,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.48.0", ] +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" From 109122ec4218b4e47862932709e55545e9fa7f54 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:02:28 -0400 Subject: [PATCH 012/233] move hash to sub-module of types --- src/{ => types}/hash.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ => types}/hash.rs (100%) diff --git a/src/hash.rs b/src/types/hash.rs similarity index 100% rename from src/hash.rs rename to src/types/hash.rs From 4e7e86cdf759bd2b1f9b03aaa412e4c50742e618 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:28:24 -0400 Subject: [PATCH 013/233] new minimal hash module --- src/types/hash.rs | 413 ++++++++++------------------------------------ 1 file changed, 91 insertions(+), 322 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index c97d79c..7e7aadb 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -1,362 +1,131 @@ -//! Fixed-size hashes -use rustc_hex::{FromHex, FromHexError, ToHex}; -use serde; -use serde::de::Unexpected; -use std::cmp::Ordering; -use std::hash::{Hash, Hasher}; +use serde::{Deserialize, Serialize, Deserializer, Serializer}; +use std::convert::TryFrom; use std::str::FromStr; -use std::{cmp, fmt, ops, str}; - -// FIXME; most of this can be removed -// This is mostly a direct copy of the H256 type from Komodo DeFi Framework - -// FIXME H256::from() has unhandled unwrap() calls -// eg, H256::from("0") - -macro_rules! impl_global_hash { - ($name: ident, $size: expr) => { - #[derive(Copy)] - #[repr(C)] - pub struct $name([u8; $size]); - - impl Default for $name { - fn default() -> Self { $name([0u8; $size]) } - } - - impl AsRef<$name> for $name { - fn as_ref(&self) -> &$name { self } - } - - impl AsRef<[u8]> for $name { - fn as_ref(&self) -> &[u8] { &self.0 } - } - - impl Clone for $name { - fn clone(&self) -> Self { - let mut result = Self::default(); - result.copy_from_slice(&self.0); - result - } - } - - impl From<[u8; $size]> for $name { - fn from(h: [u8; $size]) -> Self { $name(h) } - } - - impl From<$name> for [u8; $size] { - fn from(h: $name) -> Self { h.0 } - } - - impl<'a> From<&'a [u8]> for $name { - fn from(slc: &[u8]) -> Self { - let mut inner = [0u8; $size]; - inner[..].clone_from_slice(&slc[0..$size]); - $name(inner) - } - } - - impl From<&'static str> for $name { - fn from(s: &'static str) -> Self { s.parse().unwrap() } - } - - impl From for $name { - fn from(v: u8) -> Self { - let mut result = Self::default(); - result.0[0] = v; - result - } - } - - impl str::FromStr for $name { - type Err = FromHexError; - - fn from_str(s: &str) -> Result { - let vec: Vec = s.from_hex()?; - match vec.len() { - $size => { - let mut result = [0u8; $size]; - result.copy_from_slice(&vec); - Ok($name(result)) - }, - _ => Err(FromHexError::InvalidHexLength), - } - } - } - - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.0.to_hex::()) } - } - - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.0.to_hex::()) } - } - - impl ops::Deref for $name { - type Target = [u8; $size]; - - fn deref(&self) -> &Self::Target { &self.0 } - } - - impl ops::DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } - } - - impl cmp::PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref == other_ref - } - } - - impl cmp::PartialEq<&$name> for $name { - fn eq(&self, other: &&Self) -> bool { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref == other_ref - } - } - - impl cmp::PartialOrd for $name { - fn partial_cmp(&self, other: &Self) -> Option { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref.partial_cmp(other_ref) - } - } - - impl Hash for $name { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - state.write(&self.0); - state.finish(); - } - } - - impl Eq for $name {} - - impl $name { - pub fn take(self) -> [u8; $size] { self.0 } - - pub fn as_slice(&self) -> &[u8] { &self.0 } - - pub fn reversed(&self) -> Self { - let mut result = self.clone(); - result.reverse(); - result - } - - pub fn size() -> usize { $size } - - pub fn is_zero(&self) -> bool { self.0.iter().all(|b| *b == 0) } - } - }; +use std::fmt; +use thiserror::Error; + +#[derive(Debug, Deserialize, Serialize, Error)] +pub enum ParseHashError { + #[error("expected hex string prefixed with 'h:', found {0}")] + InvalidPrefix(String), + #[error("expected hex string, found {0}")] + InvalidHex(String), + #[error("expected 32 byte hex string, found {0}")] + InvalidLength(String), } +#[derive(Clone, Copy, PartialEq)] +pub struct H256(pub [u8; 32]); -macro_rules! impl_hash { - ($name: ident, $other: ident, $size: expr) => { - /// Hash serialization - #[derive(Clone, Copy)] - pub struct $name(pub [u8; $size]); - - impl $name { - pub const fn const_default() -> $name { $name([0; $size]) } - } - - impl Default for $name { - fn default() -> Self { $name::const_default() } - } - - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{:02x}", self) } - } +impl H256 { + pub const fn const_default() -> H256 { H256([0; 32]) } - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{:02x}", self) } + // Method for parsing a hex string without the "h:" prefix + pub fn from_str_no_prefix(hex_str: &str) -> Result { + if hex_str.len() != 64 { + return Err(ParseHashError::InvalidLength(hex_str.to_string())); } - impl From for $name - where - $other: From, - { - fn from(o: T) -> Self { $name($other::from(o).take()) } + let mut bytes = [0u8; 32]; + match hex::decode_to_slice(hex_str, &mut bytes) { + Ok(_) => Ok(H256(bytes)), + Err(_) => Err(ParseHashError::InvalidHex(hex_str.to_string())), } + } +} - impl FromStr for $name { - type Err = <$other as FromStr>::Err; +impl Default for H256 { + fn default() -> Self { H256::const_default() } +} - fn from_str(s: &str) -> Result { - let other = $other::from_str(s)?; - Ok($name(other.take())) - } - } +impl fmt::Display for H256 { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "h:{:02x}", self) } +} - #[allow(clippy::from_over_into)] - impl Into<$other> for $name { - fn into(self) -> $other { $other::from(self.0) } - } +impl fmt::Debug for H256 { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } +} - #[allow(clippy::from_over_into)] - impl Into> for $name { - fn into(self) -> Vec { self.0.to_vec() } +impl fmt::LowerHex for H256 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + for byte in &self.0 { + write!(f, "{:02x}", byte)?; } + Ok(()) + } +} - impl Eq for $name {} - - impl Ord for $name { - fn cmp(&self, other: &Self) -> Ordering { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref.cmp(other_ref) - } - } +impl Serialize for H256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref == other_ref - } - } +impl<'de> Deserialize<'de> for H256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct H256Visitor; - impl PartialOrd for $name { - fn partial_cmp(&self, other: &Self) -> Option { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref.partial_cmp(other_ref) - } - } + impl<'de> serde::de::Visitor<'de> for H256Visitor { + type Value = H256; - impl Hash for $name { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - $other::from(self.0.clone()).hash(state) + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string prefixed with 'h:' and followed by a 32 byte hex string") } - } - impl serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result + fn visit_str(self, value: &str) -> Result where - S: serde::Serializer, + E: serde::de::Error, { - let mut hex = String::new(); - hex.push_str(&$other::from(self.0.clone()).to_hex::()); - serializer.serialize_str(&hex) + H256::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } } - impl<'a> serde::Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> - where - D: serde::Deserializer<'a>, - { - struct HashVisitor; - - impl<'b> serde::de::Visitor<'b> for HashVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a hash string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if value.len() != $size * 2 { - return Err(E::invalid_value(Unexpected::Str(value), &self)); - } - - match value[..].from_hex::>() { - Ok(ref v) => { - let mut result = [0u8; $size]; - result.copy_from_slice(v); - Ok($name($other::from(result).take())) - }, - _ => Err(E::invalid_value(Unexpected::Str(value), &self)), - } - } - - fn visit_string(self, value: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(value.as_ref()) - } - } + deserializer.deserialize_str(H256Visitor) + } +} - deserializer.deserialize_identifier(HashVisitor) - } - } +impl FromStr for H256 { + type Err = ParseHashError; - impl ::core::fmt::LowerHex for $name { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - for i in &self.0[..] { - write!(f, "{:02x}", i)?; - } - Ok(()) - } + fn from_str(value: &str) -> Result { + if let Some(hex_str) = value.strip_prefix("h:") { + H256::from_str_no_prefix(hex_str) + } else { + Err(ParseHashError::InvalidPrefix(value.to_string())) } - }; -} - -impl H256 { - #[inline] - pub fn reversed(&self) -> Self { - let mut result = *self; - result.0.reverse(); - result } } -impl GlobalH256 { - #[inline] - pub fn from_reversed_str(s: &'static str) -> Self { GlobalH256::from(s).reversed() } +impl TryFrom<&str> for H256 { + type Error = ParseHashError; - #[inline] - pub fn to_reversed_str(self) -> String { self.reversed().to_string() } + fn try_from(value: &str) -> Result { + H256::from_str(value) + } } -impl_global_hash!(GlobalH256, 32); -impl_hash!(H256, GlobalH256, 32); - #[cfg(test)] mod tests { - use super::{GlobalH256, H256}; + use super::*; use std::str::FromStr; - #[test] - fn hash_debug() { - let str_reversed = "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; - let reversed_hash = H256::from(str_reversed); - let debug_result = format!("{:?}", reversed_hash); - assert_eq!(debug_result, str_reversed); - } - - #[test] - fn hash_from_str() { - let str_reversed = "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; - match H256::from_str(str_reversed) { - Ok(reversed_hash) => assert_eq!(format!("{:?}", reversed_hash), str_reversed), - _ => panic!("unexpected"), - } + cross_target_test! { + fn test_placeholder_fixme() { + let str_reversed = "h:00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; + match H256::from_str(str_reversed) { + Ok(reversed_hash) => assert_eq!(format!("{:?}", reversed_hash), str_reversed), + _ => panic!("unexpected"), + } - let str_reversed = "XXXYYY"; - if H256::from_str(str_reversed).is_ok() { - panic!("unexpected"); + let str_reversed = "XXXYYY"; + if H256::from_str(str_reversed).is_ok() { + panic!("unexpected"); + } } } - - #[test] - fn hash_to_global_hash() { - let str_reversed = "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; - let reversed_hash = H256::from(str_reversed); - let global_hash = GlobalH256::from(str_reversed); - let global_converted: GlobalH256 = reversed_hash.into(); - assert_eq!(global_converted, global_hash); - } } From cd2fa34fe3462ce69d459eb382774e2e391b339d Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:29:28 -0400 Subject: [PATCH 014/233] publicly expose H256 in types module --- src/types.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types.rs b/src/types.rs index 26cd348..7ee9b25 100644 --- a/src/types.rs +++ b/src/types.rs @@ -16,6 +16,9 @@ use std::convert::TryInto; use std::fmt; use std::str::FromStr; +mod hash; +pub use hash::H256; + const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; From 0933358fd53d08243d47ce0c5ba6a0e27b4e7d75 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:30:50 -0400 Subject: [PATCH 015/233] remove PrefixedH256 from types --- src/types.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/types.rs b/src/types.rs index 7ee9b25..60940b8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,5 @@ use crate::blake2b_internal::standard_unlock_hash; -use crate::encoding::{Encodable, Encoder, PrefixedH256}; -pub use crate::hash::H256; -pub use crate::transaction::Currency; +use crate::encoding::{Encodable, Encoder}; use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; use crate::PublicKey; @@ -10,7 +8,6 @@ use chrono::{DateTime, Utc}; use hex::FromHexError; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; -use serde_with::{serde_as, FromInto}; use std::convert::From; use std::convert::TryInto; use std::fmt; @@ -124,7 +121,7 @@ impl FromStr for Address { return Err(ParseAddressError::InvalidChecksum); } - Ok(Address(H256::from(address_bytes))) + Ok(Address(H256(address_bytes))) } } @@ -170,7 +167,7 @@ impl<'de> Deserialize<'de> for BlockID { E: serde::de::Error, { if let Some(hex_str) = value.strip_prefix("bid:") { - H256::from_str(hex_str) + H256::from_str_no_prefix(hex_str) .map(BlockID) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { @@ -193,7 +190,7 @@ impl Serialize for BlockID { } impl fmt::Display for BlockID { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "bid:{}", self.0) } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "bid:{:02x}", self.0) } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -245,10 +242,8 @@ pub enum EventType { V2ContractResolution, } -#[serde_as] #[derive(Clone, Debug, Serialize)] pub struct Event { - #[serde_as(as = "FromInto")] pub id: H256, pub index: ChainIndex, pub timestamp: DateTime, @@ -268,7 +263,7 @@ impl<'de> Deserialize<'de> for Event { { #[derive(Deserialize, Debug)] struct EventHelper { - id: PrefixedH256, + id: H256, index: ChainIndex, timestamp: DateTime, #[serde(rename = "maturityHeight")] From 852178a1ac7a480101d54f1ebd163a7cf88a048d Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:32:00 -0400 Subject: [PATCH 016/233] add FIXME comment regarding pub exports --- src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.rs b/src/types.rs index 60940b8..caff61a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; + // FIXME expose these types or the entire module use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; use crate::PublicKey; From 512a7e71863a4ab6ae5f73c864deabc598f5da21 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:32:19 -0400 Subject: [PATCH 017/233] temp fix for Currency type export --- src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.rs b/src/types.rs index caff61a..6e278ac 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,6 +3,7 @@ use crate::encoding::{Encodable, Encoder}; // FIXME expose these types or the entire module use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; +pub use crate::transaction::Currency; // FIXME should be imported via the transaction module use crate::PublicKey; use blake2b_simd::Params; use chrono::{DateTime, Utc}; From fd875453062adb2c8cfa1cd572741abd61f48426 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:34:38 -0400 Subject: [PATCH 018/233] fix H256 usage in various unit tests --- src/blake2b_internal.rs | 36 +++++++++------- src/encoding.rs | 18 ++++---- src/http/client/native.rs | 2 +- src/tests/serde.rs | 5 +-- src/tests/spend_policy.rs | 5 ++- src/tests/transaction.rs | 87 ++++++++++++++++++++------------------- 6 files changed, 79 insertions(+), 74 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 39c756b..8d742e3 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -6,7 +6,7 @@ use blake2b_simd::Params; use std::default::Default; #[cfg(test)] use hex; -#[cfg(test)] use std::convert::TryInto; +#[cfg(test)] use std::convert::{TryInto, TryFrom}; const LEAF_HASH_PREFIX: [u8; 1] = [0u8]; const NODE_HASH_PREFIX: [u8; 1] = [1u8]; @@ -129,8 +129,10 @@ pub fn standard_unlock_hash(pubkey: &PublicKey) -> H256 { pub fn hash_blake2b_single(preimage: &[u8]) -> H256 { let hash = Params::new().hash_length(32).to_state().update(preimage).finalize(); - let ret_array = hash.as_bytes(); - ret_array[0..32].into() + let mut array = [0u8; 32]; + debug_assert!(hash.as_bytes().len() == 32); + array.copy_from_slice(hash.as_bytes()); + H256(array) } fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> H256 { @@ -141,8 +143,10 @@ fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> H256 { .update(leaf1) .update(leaf2) .finalize(); - let ret_array = hash.as_bytes(); - ret_array[0..32].into() + let mut array = [0u8; 32]; + debug_assert!(hash.as_bytes().len() == 32); + array.copy_from_slice(hash.as_bytes()); + H256(array) } #[test] @@ -150,7 +154,7 @@ fn test_accumulator_new() { let default_accumulator = Accumulator::default(); let expected = Accumulator { - trees: [H256::from("0000000000000000000000000000000000000000000000000000000000000000"); 64], + trees: [H256::default(); 64], num_leaves: 0, }; assert_eq!(default_accumulator, expected) @@ -176,7 +180,7 @@ fn test_accumulator_root() { let sigs_required_leaf = sigs_required_leaf(1u64); accumulator.add_leaf(sigs_required_leaf); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); + let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(accumulator.root(), expected); } @@ -198,7 +202,7 @@ fn test_accumulator_add_leaf_standard_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); + let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(root, expected) } @@ -227,7 +231,7 @@ fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::from("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7"); + let expected = H256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(root, expected) } @@ -256,7 +260,7 @@ fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::from("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585"); + let expected = H256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(root, expected) } @@ -268,7 +272,7 @@ fn test_standard_unlock_hash() { .unwrap(); let hash = standard_unlock_hash(&pubkey); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); + let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -284,28 +288,28 @@ fn test_hash_blake2b_pair() { .unwrap(); let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); + let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } #[test] fn test_timelock_leaf() { let hash = timelock_leaf(0); - let expected = H256::from(STANDARD_TIMELOCK_BLAKE2B_HASH); + let expected = H256(STANDARD_TIMELOCK_BLAKE2B_HASH); assert_eq!(hash, expected) } #[test] fn test_sigs_required_leaf() { let hash = sigs_required_leaf(1u64); - let expected = H256::from(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); + let expected = H256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); assert_eq!(hash, expected) } #[test] fn test_hash_blake2b_single() { let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = H256::from("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b"); + let expected = H256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } @@ -317,6 +321,6 @@ fn test_public_key_leaf() { .unwrap(); let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let expected = H256::from("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b"); + let expected = H256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } diff --git a/src/encoding.rs b/src/encoding.rs index 77689d3..bf7889b 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -317,7 +317,7 @@ impl Encodable for H256 { fn test_encoder_default_hash() { assert_eq!( Encoder::default().hash(), - H256::from("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") + H256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() ) } @@ -327,7 +327,7 @@ fn test_encoder_write_bytes() { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - H256::from("d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5") + H256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() ); } @@ -337,7 +337,7 @@ fn test_encoder_write_u8() { encoder.write_u8(1); assert_eq!( encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") + H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -347,7 +347,7 @@ fn test_encoder_write_u64() { encoder.write_u64(1); assert_eq!( encoder.hash(), - H256::from("1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b") + H256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() ); } @@ -357,7 +357,7 @@ fn test_encoder_write_distiguisher() { encoder.write_distinguisher("test"); assert_eq!( encoder.hash(), - H256::from("25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c") + H256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() ); } @@ -367,7 +367,7 @@ fn test_encoder_write_bool() { encoder.write_bool(true); assert_eq!( encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") + H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -377,14 +377,14 @@ fn test_encoder_reset() { encoder.write_bool(true); assert_eq!( encoder.hash(), - H256::from("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25") + H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); encoder.reset(); encoder.write_bool(false); assert_eq!( encoder.hash(), - H256::from("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314") + H256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() ); } @@ -397,6 +397,6 @@ fn test_encoder_complex() { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - H256::from("b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221") + H256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() ); } diff --git a/src/http/client/native.rs b/src/http/client/native.rs index 47400de..960995a 100644 --- a/src/http/client/native.rs +++ b/src/http/client/native.rs @@ -166,7 +166,7 @@ mod tests { async fn test_api_events() { use crate::types::H256; let request = GetEventRequest { - txid: H256::from_str("77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), + txid: H256::from_str("h:77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), }; let _response = test_dispatch(request).await; } diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 4154e03..2264873 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,7 +1,6 @@ -use crate::encoding::PrefixedH256; use crate::spend_policy::UnlockKey; use crate::transaction::{SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; -use crate::types::{Address, BlockID, Event}; +use crate::types::{Address, BlockID, Event, H256}; // Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) macro_rules! test_serde { @@ -47,7 +46,7 @@ fn test_serde_unlock_key() { #[test] fn test_serde_sia_hash() { test_serde!( - PrefixedH256, + H256, json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") ); } diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 43cefd2..0e556cc 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,6 +1,7 @@ use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, H256}; use crate::PublicKey; +use std::convert::TryFrom; use std::str::FromStr; #[test] @@ -59,7 +60,7 @@ fn test_serde_spend_policy_hash() { "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" } ); - let hash = H256::from("0102030000000000000000000000000000000000000000000000000000000000"); + let hash = H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Hash(hash); @@ -93,7 +94,7 @@ fn test_serde_spend_policy_threshold() { ) .unwrap(); - let secret_hash = H256::from("0100000000000000000000000000000000000000000000000000000000000000"); + let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let j = json!( diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index d10a062..22eda9c 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -6,6 +6,7 @@ use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRev StateElement, V2FileContract, V2FileContractElement, V2Transaction}; use crate::types::{v1_standard_address_from_pubkey, Address, H256}; use crate::{PublicKey, Signature}; +use std::convert::TryFrom; use std::str::FromStr; #[test] @@ -17,12 +18,12 @@ fn test_siacoin_input_encode() { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: H256::from("0405060000000000000000000000000000000000000000000000000000000000"), + parent_id: H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), unlock_condition, }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::from("1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4"); + let expected = H256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); assert_eq!(hash, expected); } @@ -31,7 +32,7 @@ fn test_siacoin_currency_encode_v1() { let currency: Currency = 1.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = H256::from("a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826"); + let expected = H256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); assert_eq!(hash, expected); } @@ -40,7 +41,7 @@ fn test_siacoin_currency_encode_v2() { let currency: Currency = 1.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = H256::from("a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d"); + let expected = H256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -49,7 +50,7 @@ fn test_siacoin_currency_encode_v1_max() { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = H256::from("4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb"); + let expected = H256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); assert_eq!(hash, expected); } @@ -58,7 +59,7 @@ fn test_siacoin_currency_encode_v2_max() { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = H256::from("681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4"); + let expected = H256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); assert_eq!(hash, expected); } @@ -71,7 +72,7 @@ fn test_siacoin_output_encode_v1() { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); - let expected = H256::from("3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28"); + let expected = H256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); assert_eq!(hash, expected); } @@ -84,18 +85,18 @@ fn test_siacoin_output_encode_v2() { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); - let expected = H256::from("c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3"); + let expected = H256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); assert_eq!(hash, expected); } #[test] fn test_siacoin_element_encode() { let state_element = StateElement { - id: H256::from("0102030000000000000000000000000000000000000000000000000000000000"), + id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::from("0405060000000000000000000000000000000000000000000000000000000000"), - H256::from("0708090000000000000000000000000000000000000000000000000000000000"), + H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let siacoin_element = SiacoinElement { @@ -111,36 +112,36 @@ fn test_siacoin_element_encode() { }; let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = H256::from("3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e"); + let expected = H256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); assert_eq!(hash, expected); } #[test] fn test_state_element_encode() { let state_element = StateElement { - id: H256::from("0102030000000000000000000000000000000000000000000000000000000000"), + id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::from("0405060000000000000000000000000000000000000000000000000000000000"), - H256::from("0708090000000000000000000000000000000000000000000000000000000000"), + H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let hash = Encoder::encode_and_hash(&state_element); - let expected = H256::from("bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468"); + let expected = H256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); assert_eq!(hash, expected); } #[test] fn test_state_element_encode_null_merkle_proof() { let state_element = StateElement { - id: H256::from("0102030000000000000000000000000000000000000000000000000000000000"), + id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: None, }; let hash = Encoder::encode_and_hash(&state_element); - let expected = H256::from("d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e"); + let expected = H256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); assert_eq!(hash, expected); } @@ -152,7 +153,7 @@ fn test_siacoin_input_encode_v1() { }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::from("2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0"); + let expected = H256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); assert_eq!(hash, expected); } @@ -162,7 +163,7 @@ fn test_signature_encode() { &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let hash = Encoder::encode_and_hash(&signature); - let expected = H256::from("1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7"); + let expected = H256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); assert_eq!(hash, expected); } @@ -185,7 +186,7 @@ fn test_satisfied_policy_encode_public_key() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822"); + let expected = H256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); assert_eq!(hash, expected); } @@ -200,7 +201,7 @@ fn test_satisfied_policy_encode_hash_empty() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44"); + let expected = H256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); assert_eq!(hash, expected); } @@ -217,7 +218,7 @@ fn test_satisfied_policy_encode_hash_frivulous_signature() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9"); + let expected = H256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); assert_eq!(hash, expected); } @@ -232,7 +233,7 @@ fn test_satisfied_policy_encode_hash() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9"); + let expected = H256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); assert_eq!(hash, expected); } @@ -257,7 +258,7 @@ fn test_satisfied_policy_encode_unlock_condition_standard() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f"); + let expected = H256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); assert_eq!(hash, expected); } @@ -294,7 +295,7 @@ fn test_satisfied_policy_encode_unlock_condition_complex() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901"); + let expected = H256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); assert_eq!(hash, expected); } @@ -313,7 +314,7 @@ fn test_satisfied_policy_encode_threshold_simple() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d"); + let expected = H256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); assert_eq!(hash, expected); } @@ -328,7 +329,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_success() { ) .unwrap(); - let secret_hash = H256::from("0100000000000000000000000000000000000000000000000000000000000000"); + let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -341,7 +342,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_success() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86"); + let expected = H256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); assert_eq!(hash, expected); } @@ -356,7 +357,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { ) .unwrap(); - let secret_hash = H256::from("0100000000000000000000000000000000000000000000000000000000000000"); + let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = spend_policy_atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -369,7 +370,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::from("8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348"); + let expected = H256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); assert_eq!(hash, expected); } @@ -404,7 +405,7 @@ fn test_siacoin_input_encode_v2() { }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::from("a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4"); + let expected = H256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); assert_eq!(hash, expected); } @@ -425,7 +426,7 @@ fn test_attestation_encode() { }; let hash = Encoder::encode_and_hash(&attestation); - let expected = H256::from("b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d"); + let expected = H256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); assert_eq!(hash, expected); } @@ -474,7 +475,7 @@ fn test_file_contract_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = H256::from("6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c"); + let expected = H256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); assert_eq!(hash, expected); } @@ -523,11 +524,11 @@ fn test_file_contract_element_v2_encode() { }; let state_element = StateElement { - id: H256::from("0102030000000000000000000000000000000000000000000000000000000000"), + id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::from("0405060000000000000000000000000000000000000000000000000000000000"), - H256::from("0708090000000000000000000000000000000000000000000000000000000000"), + H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -537,7 +538,7 @@ fn test_file_contract_element_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = H256::from("4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039"); + let expected = H256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); assert_eq!(hash, expected); } @@ -586,11 +587,11 @@ fn test_file_contract_revision_v2_encode() { }; let state_element = StateElement { - id: H256::from("0102030000000000000000000000000000000000000000000000000000000000"), + id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::from("0405060000000000000000000000000000000000000000000000000000000000"), - H256::from("0708090000000000000000000000000000000000000000000000000000000000"), + H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -605,7 +606,7 @@ fn test_file_contract_revision_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = H256::from("22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6"); + let expected = H256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); assert_eq!(hash, expected); } @@ -665,7 +666,7 @@ fn test_v2_transaction_sig_hash() { let tx = serde_json::from_value::(j).unwrap(); let hash = tx.input_sig_hash(); - let expected = H256::from("ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac"); + let expected = H256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); assert_eq!(hash, expected); } From 412a6fa35c61f763f2916a288b456eae14017684 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:35:44 -0400 Subject: [PATCH 019/233] fix H256 usage in more unit tests --- src/tests/encoding.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index ae2524c..5c2771f 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -3,6 +3,7 @@ use crate::encoding::Encoder; use crate::spend_policy::{SpendPolicy, UnlockCondition}; use crate::types::{Address, H256}; use crate::PublicKey; +use std::convert::TryFrom; use std::str::FromStr; #[test] @@ -18,7 +19,7 @@ fn test_unlock_condition_unlock_hash_2of2_multisig() { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); let hash = unlock_condition.unlock_hash(); - let expected = H256::from("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7"); + let expected = H256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(hash, expected); } @@ -35,7 +36,7 @@ fn test_unlock_condition_unlock_hash_1of2_multisig() { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = H256::from("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585"); + let expected = H256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(hash, expected); } @@ -44,7 +45,7 @@ fn test_spend_policy_encode_above() { let policy = SpendPolicy::above(1); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::from("bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6"); + let expected = H256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -57,7 +58,7 @@ fn test_spend_policy_encode_above() { fn test_spend_policy_encode_after() { let policy = SpendPolicy::after(1); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::from("07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311"); + let expected = H256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -75,7 +76,7 @@ fn test_spend_policy_encode_pubkey() { let policy = SpendPolicy::PublicKey(pubkey); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::from("4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe"); + let expected = H256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -86,11 +87,11 @@ fn test_spend_policy_encode_pubkey() { #[test] fn test_spend_policy_encode_hash() { - let hash = H256::from("0102030000000000000000000000000000000000000000000000000000000000"); + let hash = H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::Hash(hash); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::from("9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a"); + let expected = H256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -107,7 +108,7 @@ fn test_spend_policy_encode_threshold() { }; let hash = Encoder::encode_and_hash(&policy); - let expected = H256::from("7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802"); + let expected = H256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -149,7 +150,7 @@ fn test_unlock_condition_encode() { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = Encoder::encode_and_hash(&unlock_condition); - let expected = H256::from("5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98"); + let expected = H256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); assert_eq!(hash, expected); } @@ -161,7 +162,7 @@ fn test_public_key_encode() { .unwrap(); let hash = Encoder::encode_and_hash(&public_key); - let expected = H256::from("d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726"); + let expected = H256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); assert_eq!(hash, expected); } @@ -174,7 +175,7 @@ fn test_unlock_condition_unlock_hash_standard() { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = H256::from("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d"); + let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected); let hash = standard_unlock_hash(&pubkey); From dab14e477dd37d870713a62ad5ed2dd2e44a65a4 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:37:06 -0400 Subject: [PATCH 020/233] add macros module; remove hash from root exposed modules --- src/lib.rs | 4 +++- src/macros.rs | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/macros.rs diff --git a/src/lib.rs b/src/lib.rs index 14013a4..b809f34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +mod macros; + use derive_more::Display; use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, Signer}; @@ -8,7 +11,6 @@ use std::str::FromStr; pub mod blake2b_internal; pub mod encoding; -pub mod hash; pub mod http; pub mod specifier; pub mod spend_policy; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..f03365a --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,15 @@ +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::wasm_bindgen_test; + +// Run a unit test on both wasm32 and non-wasm32 targets +macro_rules! cross_target_test { + ($test:item) => { + #[cfg(target_arch = "wasm32")] + #[wasm_bindgen_test] + $test + + #[cfg(not(target_arch = "wasm32"))] + #[test] + $test + }; +} \ No newline at end of file From 332766174c7f8492272244d3fb8f3e5886cc813e Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:37:48 -0400 Subject: [PATCH 021/233] add FIXME comment regarding lib structure --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index b809f34..199bd5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub enum KeypairError { #[macro_use] extern crate serde_json; +// FIXME move everything below this to types mod pub struct Keypair(pub Ed25519Keypair); impl Keypair { From 5113a45ca227bb1444dd516c1448a6e2223868a1 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:40:15 -0400 Subject: [PATCH 022/233] remove PrefixedH256 type --- src/encoding.rs | 58 --------------------------------------------- src/spend_policy.rs | 8 +++---- src/transaction.rs | 6 +---- 3 files changed, 5 insertions(+), 67 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index bf7889b..96ae333 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -193,64 +193,6 @@ impl From for PrefixedPublicKey { fn from(public_key: PublicKey) -> Self { PrefixedPublicKey(public_key) } } -/// This wrapper allows us to use H256 internally but still serde as "h:" prefixed string -#[derive(Clone, Debug, PartialEq)] -pub struct PrefixedH256(pub H256); - -// FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros -impl<'de> Deserialize<'de> for PrefixedH256 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct PrefixedH256Visitor; - - impl<'de> serde::de::Visitor<'de> for PrefixedH256Visitor { - type Value = PrefixedH256; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'h:' and followed by a 64-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("h:") { - H256::from_str(hex_str) - .map(PrefixedH256) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(PrefixedH256Visitor) - } -} - -impl Serialize for PrefixedH256 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{}", self)) - } -} - -impl fmt::Display for PrefixedH256 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } -} - -impl From for H256 { - fn from(sia_hash: PrefixedH256) -> Self { sia_hash.0 } -} - -impl From for PrefixedH256 { - fn from(h256: H256) -> Self { PrefixedH256(h256) } -} - /// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string #[derive(Clone, Debug, PartialEq)] pub struct ScoidH256(pub H256); diff --git a/src/spend_policy.rs b/src/spend_policy.rs index 4f043af..1f65b7e 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -1,5 +1,5 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, Accumulator}; -use crate::encoding::{Encodable, Encoder, PrefixedH256, PrefixedPublicKey}; +use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; use crate::specifier::Specifier; use crate::transaction::{Preimage, SatisfiedPolicy}; use crate::types::{Address, H256}; @@ -33,7 +33,7 @@ pub enum SpendPolicyHelper { Above(u64), After(u64), Pk(PrefixedPublicKey), - H(PrefixedH256), + H(H256), Thresh { n: u8, of: Vec }, Opaque(Address), Uc(UnlockCondition), // For v1 compatibility @@ -45,7 +45,7 @@ impl From for SpendPolicy { SpendPolicyHelper::Above(height) => SpendPolicy::Above(height), SpendPolicyHelper::After(time) => SpendPolicy::After(time), SpendPolicyHelper::Pk(pk) => SpendPolicy::PublicKey(pk.0), - SpendPolicyHelper::H(hash) => SpendPolicy::Hash(hash.0), + SpendPolicyHelper::H(hash) => SpendPolicy::Hash(hash), SpendPolicyHelper::Thresh { n, of } => SpendPolicy::Threshold { n, of: of.into_iter().map(SpendPolicy::from).collect(), @@ -62,7 +62,7 @@ impl From for SpendPolicyHelper { SpendPolicy::Above(height) => SpendPolicyHelper::Above(height), SpendPolicy::After(time) => SpendPolicyHelper::After(time), SpendPolicy::PublicKey(pk) => SpendPolicyHelper::Pk(PrefixedPublicKey(pk)), - SpendPolicy::Hash(hash) => SpendPolicyHelper::H(PrefixedH256(hash)), + SpendPolicy::Hash(hash) => SpendPolicyHelper::H(hash), SpendPolicy::Threshold { n, of } => SpendPolicyHelper::Thresh { n, of: of.into_iter().map(SpendPolicyHelper::from).collect(), diff --git a/src/transaction.rs b/src/transaction.rs index b480291..5574ae8 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedH256, PrefixedPublicKey, PrefixedSignature, ScoidH256}; +use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, PrefixedSignature, ScoidH256}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, H256}; use crate::{Keypair, PublicKey, Signature}; @@ -177,11 +177,9 @@ impl Encodable for SatisfiedPolicy { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StateElement { - #[serde_as(as = "FromInto")] pub id: H256, pub leaf_index: u64, #[serde(default)] - #[serde_as(as = "Option>>")] pub merkle_proof: Option>, } @@ -363,7 +361,6 @@ pub struct CoveredFields { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionSignature { - #[serde_as(as = "FromInto")] #[serde(rename = "parentID")] pub parent_id: H256, #[serde(default)] @@ -443,7 +440,6 @@ impl Encodable for FileContract { #[serde(rename_all = "camelCase")] pub struct V2FileContract { pub filesize: u64, - #[serde_as(as = "FromInto")] pub file_merkle_root: H256, pub proof_height: u64, pub expiration_height: u64, From 87cfa52c21fe88e8f11f011cd7669f2cf8115fea Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 20 Sep 2024 23:41:30 -0400 Subject: [PATCH 023/233] rename H256->Hash256 to match Go code --- src/blake2b_internal.rs | 52 +++++++++--------- src/encoding.rs | 36 ++++++------- src/http/client/native.rs | 4 +- src/http/endpoints.rs | 4 +- src/spend_policy.rs | 16 +++--- src/tests/encoding.rs | 24 ++++----- src/tests/serde.rs | 4 +- src/tests/spend_policy.rs | 6 +-- src/tests/transaction.rs | 110 +++++++++++++++++++------------------- src/transaction.rs | 36 ++++++------- src/types.rs | 22 ++++---- src/types/hash.rs | 38 ++++++------- 12 files changed, 176 insertions(+), 176 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 8d742e3..2f486f5 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -1,6 +1,6 @@ use crate::specifier::Specifier; use crate::spend_policy::UnlockKey; -use crate::types::H256; +use crate::types::Hash256; use crate::PublicKey; use blake2b_simd::Params; use std::default::Default; @@ -26,14 +26,14 @@ const STANDARD_SIGS_REQUIRED_BLAKE2B_HASH: [u8; 32] = [ #[derive(Debug, PartialEq)] pub struct Accumulator { - trees: [H256; 64], + trees: [Hash256; 64], num_leaves: u64, } impl Default for Accumulator { fn default() -> Self { Accumulator { - trees: [H256::default(); 64], // Initialize all bytes to zero + trees: [Hash256::default(); 64], // Initialize all bytes to zero num_leaves: 0, } } @@ -44,7 +44,7 @@ impl Accumulator { fn has_tree_at_height(&self, height: u64) -> bool { self.num_leaves & (1 << height) != 0 } // Add a leaf to the accumulator - pub fn add_leaf(&mut self, h: H256) { + pub fn add_leaf(&mut self, h: Hash256) { let mut i = 0; let mut new_hash = h; while self.has_tree_at_height(i) { @@ -56,11 +56,11 @@ impl Accumulator { } // Calulate the root hash of the Merkle tree - pub fn root(&self) -> H256 { + pub fn root(&self) -> Hash256 { // trailing_zeros determines the height Merkle tree accumulator where the current lowest single leaf is located let i = self.num_leaves.trailing_zeros() as u64; if i == 64 { - return H256::default(); // Return all zeros if no leaves + return Hash256::default(); // Return all zeros if no leaves } let mut root = self.trees[i as usize]; for j in i + 1..64 { @@ -72,7 +72,7 @@ impl Accumulator { } } -pub fn sigs_required_leaf(sigs_required: u64) -> H256 { +pub fn sigs_required_leaf(sigs_required: u64) -> Hash256 { let sigs_required_array: [u8; 8] = sigs_required.to_le_bytes(); let mut combined = Vec::new(); combined.extend_from_slice(&LEAF_HASH_PREFIX); @@ -83,7 +83,7 @@ pub fn sigs_required_leaf(sigs_required: u64) -> H256 { // public key leaf is // blake2b(leafHashPrefix + 16_byte_ascii_algorithm_identifier + public_key_length_u64 + public_key) -pub fn public_key_leaf(unlock_key: &UnlockKey) -> H256 { +pub fn public_key_leaf(unlock_key: &UnlockKey) -> Hash256 { let mut combined = Vec::new(); combined.extend_from_slice(&LEAF_HASH_PREFIX); match unlock_key { @@ -101,7 +101,7 @@ pub fn public_key_leaf(unlock_key: &UnlockKey) -> H256 { hash_blake2b_single(&combined) } -pub fn timelock_leaf(timelock: u64) -> H256 { +pub fn timelock_leaf(timelock: u64) -> Hash256 { let timelock: [u8; 8] = timelock.to_le_bytes(); let mut combined = Vec::new(); combined.extend_from_slice(&LEAF_HASH_PREFIX); @@ -117,7 +117,7 @@ pub fn timelock_leaf(timelock: u64) -> H256 { // ┌─────────┴──────────┐ // ┌─────┴─────┐ │ // timelock pubkey sigsrequired -pub fn standard_unlock_hash(pubkey: &PublicKey) -> H256 { +pub fn standard_unlock_hash(pubkey: &PublicKey) -> Hash256 { let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(*pubkey)); let timelock_pubkey_node = hash_blake2b_pair(&NODE_HASH_PREFIX, &STANDARD_TIMELOCK_BLAKE2B_HASH, &pubkey_leaf.0); hash_blake2b_pair( @@ -127,15 +127,15 @@ pub fn standard_unlock_hash(pubkey: &PublicKey) -> H256 { ) } -pub fn hash_blake2b_single(preimage: &[u8]) -> H256 { +pub fn hash_blake2b_single(preimage: &[u8]) -> Hash256 { let hash = Params::new().hash_length(32).to_state().update(preimage).finalize(); let mut array = [0u8; 32]; debug_assert!(hash.as_bytes().len() == 32); array.copy_from_slice(hash.as_bytes()); - H256(array) + Hash256(array) } -fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> H256 { +fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> Hash256 { let hash = Params::new() .hash_length(32) .to_state() @@ -146,7 +146,7 @@ fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> H256 { let mut array = [0u8; 32]; debug_assert!(hash.as_bytes().len() == 32); array.copy_from_slice(hash.as_bytes()); - H256(array) + Hash256(array) } #[test] @@ -154,14 +154,14 @@ fn test_accumulator_new() { let default_accumulator = Accumulator::default(); let expected = Accumulator { - trees: [H256::default(); 64], + trees: [Hash256::default(); 64], num_leaves: 0, }; assert_eq!(default_accumulator, expected) } #[test] -fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), H256::default()) } +fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), Hash256::default()) } #[test] fn test_accumulator_root() { @@ -180,7 +180,7 @@ fn test_accumulator_root() { let sigs_required_leaf = sigs_required_leaf(1u64); accumulator.add_leaf(sigs_required_leaf); - let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(accumulator.root(), expected); } @@ -202,7 +202,7 @@ fn test_accumulator_add_leaf_standard_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(root, expected) } @@ -231,7 +231,7 @@ fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(root, expected) } @@ -260,7 +260,7 @@ fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = H256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(root, expected) } @@ -272,7 +272,7 @@ fn test_standard_unlock_hash() { .unwrap(); let hash = standard_unlock_hash(&pubkey); - let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -288,28 +288,28 @@ fn test_hash_blake2b_pair() { .unwrap(); let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } #[test] fn test_timelock_leaf() { let hash = timelock_leaf(0); - let expected = H256(STANDARD_TIMELOCK_BLAKE2B_HASH); + let expected = Hash256(STANDARD_TIMELOCK_BLAKE2B_HASH); assert_eq!(hash, expected) } #[test] fn test_sigs_required_leaf() { let hash = sigs_required_leaf(1u64); - let expected = H256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); + let expected = Hash256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); assert_eq!(hash, expected) } #[test] fn test_hash_blake2b_single() { let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = H256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } @@ -321,6 +321,6 @@ fn test_public_key_leaf() { .unwrap(); let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let expected = H256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } diff --git a/src/encoding.rs b/src/encoding.rs index 96ae333..fe4a368 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,5 +1,5 @@ use crate::blake2b_internal::hash_blake2b_single; -use crate::types::H256; +use crate::types::Hash256; use crate::{PublicKey, Signature}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::From; @@ -67,10 +67,10 @@ impl Encoder { pub fn write_bool(&mut self, b: bool) { self.buffer.push(b as u8) } - pub fn hash(&self) -> H256 { hash_blake2b_single(&self.buffer) } + pub fn hash(&self) -> Hash256 { hash_blake2b_single(&self.buffer) } // Utility method to create, encode, and hash - pub fn encode_and_hash(item: &T) -> H256 { + pub fn encode_and_hash(item: &T) -> Hash256 { let mut encoder = Encoder::default(); item.encode(&mut encoder); encoder.hash() @@ -195,7 +195,7 @@ impl From for PrefixedPublicKey { /// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string #[derive(Clone, Debug, PartialEq)] -pub struct ScoidH256(pub H256); +pub struct ScoidH256(pub Hash256); // FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros impl<'de> Deserialize<'de> for ScoidH256 { @@ -217,7 +217,7 @@ impl<'de> Deserialize<'de> for ScoidH256 { E: serde::de::Error, { if let Some(hex_str) = value.strip_prefix("scoid:") { - H256::from_str(hex_str) + Hash256::from_str(hex_str) .map(ScoidH256) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { @@ -243,15 +243,15 @@ impl fmt::Display for ScoidH256 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } } -impl From for H256 { +impl From for Hash256 { fn from(sia_hash: ScoidH256) -> Self { sia_hash.0 } } -impl From for ScoidH256 { - fn from(h256: H256) -> Self { ScoidH256(h256) } +impl From for ScoidH256 { + fn from(h256: Hash256) -> Self { ScoidH256(h256) } } -impl Encodable for H256 { +impl Encodable for Hash256 { fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0); } } @@ -259,7 +259,7 @@ impl Encodable for H256 { fn test_encoder_default_hash() { assert_eq!( Encoder::default().hash(), - H256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() ) } @@ -269,7 +269,7 @@ fn test_encoder_write_bytes() { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - H256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() + Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() ); } @@ -279,7 +279,7 @@ fn test_encoder_write_u8() { encoder.write_u8(1); assert_eq!( encoder.hash(), - H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -289,7 +289,7 @@ fn test_encoder_write_u64() { encoder.write_u64(1); assert_eq!( encoder.hash(), - H256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() + Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() ); } @@ -299,7 +299,7 @@ fn test_encoder_write_distiguisher() { encoder.write_distinguisher("test"); assert_eq!( encoder.hash(), - H256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() + Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() ); } @@ -309,7 +309,7 @@ fn test_encoder_write_bool() { encoder.write_bool(true); assert_eq!( encoder.hash(), - H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -319,14 +319,14 @@ fn test_encoder_reset() { encoder.write_bool(true); assert_eq!( encoder.hash(), - H256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); encoder.reset(); encoder.write_bool(false); assert_eq!( encoder.hash(), - H256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() + Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() ); } @@ -339,6 +339,6 @@ fn test_encoder_complex() { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - H256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() + Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() ); } diff --git a/src/http/client/native.rs b/src/http/client/native.rs index 960995a..b7469ab 100644 --- a/src/http/client/native.rs +++ b/src/http/client/native.rs @@ -164,9 +164,9 @@ mod tests { #[tokio::test] async fn test_api_events() { - use crate::types::H256; + use crate::types::Hash256; let request = GetEventRequest { - txid: H256::from_str("h:77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), + txid: Hash256::from_str("h:77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), }; let _response = test_dispatch(request).await; } diff --git a/src/http/endpoints.rs b/src/http/endpoints.rs index 96e2549..8547532 100644 --- a/src/http/endpoints.rs +++ b/src/http/endpoints.rs @@ -1,6 +1,6 @@ use crate::http::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; use crate::transaction::{SiacoinElement, V1Transaction, V2Transaction}; -use crate::types::{Address, BlockID, Currency, Event, H256}; +use crate::types::{Address, BlockID, Currency, Event, Hash256}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -130,7 +130,7 @@ pub struct AddressBalanceResponse { /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. #[derive(Deserialize, Serialize, Debug)] pub struct GetEventRequest { - pub txid: H256, + pub txid: Hash256, } impl SiaApiRequest for GetEventRequest { diff --git a/src/spend_policy.rs b/src/spend_policy.rs index 1f65b7e..7bdd6e4 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -2,7 +2,7 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlo use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; use crate::specifier::Specifier; use crate::transaction::{Preimage, SatisfiedPolicy}; -use crate::types::{Address, H256}; +use crate::types::{Address, Hash256}; use crate::{PublicKey, Signature}; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; @@ -20,7 +20,7 @@ pub enum SpendPolicy { Above(u64), After(u64), PublicKey(PublicKey), - Hash(H256), + Hash(Hash256), Threshold { n: u8, of: Vec }, Opaque(Address), UnlockConditions(UnlockCondition), // For v1 compatibility @@ -33,7 +33,7 @@ pub enum SpendPolicyHelper { Above(u64), After(u64), Pk(PrefixedPublicKey), - H(H256), + H(Hash256), Thresh { n: u8, of: Vec }, Opaque(Address), Uc(UnlockCondition), // For v1 compatibility @@ -162,7 +162,7 @@ impl SpendPolicy { pub fn public_key(pk: PublicKey) -> Self { SpendPolicy::PublicKey(pk) } - pub fn hash(h: H256) -> Self { SpendPolicy::Hash(h) } + pub fn hash(h: Hash256) -> Self { SpendPolicy::Hash(h) } pub fn threshold(n: u8, of: Vec) -> Self { SpendPolicy::Threshold { n, of } } @@ -218,7 +218,7 @@ impl SatisfyPolicy for () { } } -pub fn spend_policy_atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: H256) -> SpendPolicy { +pub fn spend_policy_atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { let policy_after = SpendPolicy::After(lock_time); let policy_hash = SpendPolicy::Hash(hash); @@ -238,7 +238,7 @@ pub fn spend_policy_atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64 } } -pub fn spend_policy_atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: H256) -> SpendPolicy { +pub fn spend_policy_atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { match spend_policy_atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[1] = of[1].opacify(); @@ -248,7 +248,7 @@ pub fn spend_policy_atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_t } } -pub fn spend_policy_atomic_swap_refund(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: H256) -> SpendPolicy { +pub fn spend_policy_atomic_swap_refund(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { match spend_policy_atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[0] = of[0].opacify(); @@ -422,7 +422,7 @@ impl UnlockCondition { } } - pub fn unlock_hash(&self) -> H256 { + pub fn unlock_hash(&self) -> Hash256 { // almost all UnlockConditions are standard, so optimize for that case if let UnlockKey::Ed25519(public_key) = &self.unlock_keys[0] { if self.timelock == 0 && self.unlock_keys.len() == 1 && self.signatures_required == 1 { diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 5c2771f..5288142 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -1,7 +1,7 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::Encoder; use crate::spend_policy::{SpendPolicy, UnlockCondition}; -use crate::types::{Address, H256}; +use crate::types::{Address, Hash256}; use crate::PublicKey; use std::convert::TryFrom; use std::str::FromStr; @@ -19,7 +19,7 @@ fn test_unlock_condition_unlock_hash_2of2_multisig() { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); let hash = unlock_condition.unlock_hash(); - let expected = H256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(hash, expected); } @@ -36,7 +36,7 @@ fn test_unlock_condition_unlock_hash_1of2_multisig() { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = H256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(hash, expected); } @@ -45,7 +45,7 @@ fn test_spend_policy_encode_above() { let policy = SpendPolicy::above(1); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); + let expected = Hash256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -58,7 +58,7 @@ fn test_spend_policy_encode_above() { fn test_spend_policy_encode_after() { let policy = SpendPolicy::after(1); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); + let expected = Hash256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -76,7 +76,7 @@ fn test_spend_policy_encode_pubkey() { let policy = SpendPolicy::PublicKey(pubkey); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); + let expected = Hash256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -87,11 +87,11 @@ fn test_spend_policy_encode_pubkey() { #[test] fn test_spend_policy_encode_hash() { - let hash = H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::Hash(hash); let hash = Encoder::encode_and_hash(&policy); - let expected = H256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); + let expected = Hash256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -108,7 +108,7 @@ fn test_spend_policy_encode_threshold() { }; let hash = Encoder::encode_and_hash(&policy); - let expected = H256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); + let expected = Hash256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -150,7 +150,7 @@ fn test_unlock_condition_encode() { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = Encoder::encode_and_hash(&unlock_condition); - let expected = H256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); + let expected = Hash256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); assert_eq!(hash, expected); } @@ -162,7 +162,7 @@ fn test_public_key_encode() { .unwrap(); let hash = Encoder::encode_and_hash(&public_key); - let expected = H256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); + let expected = Hash256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); assert_eq!(hash, expected); } @@ -175,7 +175,7 @@ fn test_unlock_condition_unlock_hash_standard() { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = H256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected); let hash = standard_unlock_hash(&pubkey); diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 2264873..a2f40b2 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,6 +1,6 @@ use crate::spend_policy::UnlockKey; use crate::transaction::{SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; -use crate::types::{Address, BlockID, Event, H256}; +use crate::types::{Address, BlockID, Event, Hash256}; // Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) macro_rules! test_serde { @@ -46,7 +46,7 @@ fn test_serde_unlock_key() { #[test] fn test_serde_sia_hash() { test_serde!( - H256, + Hash256, json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") ); } diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 0e556cc..bff2c50 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,5 +1,5 @@ use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, H256}; +use crate::types::{Address, Hash256}; use crate::PublicKey; use std::convert::TryFrom; use std::str::FromStr; @@ -60,7 +60,7 @@ fn test_serde_spend_policy_hash() { "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" } ); - let hash = H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Hash(hash); @@ -94,7 +94,7 @@ fn test_serde_spend_policy_threshold() { ) .unwrap(); - let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let j = json!( diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 22eda9c..b462d26 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -4,7 +4,7 @@ use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_s use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, StateElement, V2FileContract, V2FileContractElement, V2Transaction}; -use crate::types::{v1_standard_address_from_pubkey, Address, H256}; +use crate::types::{v1_standard_address_from_pubkey, Address, Hash256}; use crate::{PublicKey, Signature}; use std::convert::TryFrom; use std::str::FromStr; @@ -18,12 +18,12 @@ fn test_siacoin_input_encode() { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), unlock_condition, }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); + let expected = Hash256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); assert_eq!(hash, expected); } @@ -32,7 +32,7 @@ fn test_siacoin_currency_encode_v1() { let currency: Currency = 1.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = H256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); + let expected = Hash256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); assert_eq!(hash, expected); } @@ -41,7 +41,7 @@ fn test_siacoin_currency_encode_v2() { let currency: Currency = 1.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = H256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + let expected = Hash256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -50,7 +50,7 @@ fn test_siacoin_currency_encode_v1_max() { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = H256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); + let expected = Hash256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); assert_eq!(hash, expected); } @@ -59,7 +59,7 @@ fn test_siacoin_currency_encode_v2_max() { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = H256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); + let expected = Hash256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); assert_eq!(hash, expected); } @@ -72,7 +72,7 @@ fn test_siacoin_output_encode_v1() { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); - let expected = H256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); + let expected = Hash256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); assert_eq!(hash, expected); } @@ -85,18 +85,18 @@ fn test_siacoin_output_encode_v2() { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); - let expected = H256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); + let expected = Hash256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); assert_eq!(hash, expected); } #[test] fn test_siacoin_element_encode() { let state_element = StateElement { - id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let siacoin_element = SiacoinElement { @@ -112,48 +112,48 @@ fn test_siacoin_element_encode() { }; let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = H256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); + let expected = Hash256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); assert_eq!(hash, expected); } #[test] fn test_state_element_encode() { let state_element = StateElement { - id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let hash = Encoder::encode_and_hash(&state_element); - let expected = H256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); + let expected = Hash256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); assert_eq!(hash, expected); } #[test] fn test_state_element_encode_null_merkle_proof() { let state_element = StateElement { - id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: None, }; let hash = Encoder::encode_and_hash(&state_element); - let expected = H256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); + let expected = Hash256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); assert_eq!(hash, expected); } #[test] fn test_siacoin_input_encode_v1() { let vin = SiacoinInputV1 { - parent_id: H256::default(), + parent_id: Hash256::default(), unlock_condition: UnlockCondition::new(vec![], 0, 0), }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); + let expected = Hash256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); assert_eq!(hash, expected); } @@ -163,7 +163,7 @@ fn test_signature_encode() { &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let hash = Encoder::encode_and_hash(&signature); - let expected = H256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); + let expected = Hash256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); assert_eq!(hash, expected); } @@ -186,13 +186,13 @@ fn test_satisfied_policy_encode_public_key() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); + let expected = Hash256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); assert_eq!(hash, expected); } #[test] fn test_satisfied_policy_encode_hash_empty() { - let policy = SpendPolicy::Hash(H256::default()); + let policy = SpendPolicy::Hash(Hash256::default()); let satisfied_policy = SatisfiedPolicy { policy, @@ -201,14 +201,14 @@ fn test_satisfied_policy_encode_hash_empty() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); + let expected = Hash256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); assert_eq!(hash, expected); } // Adding a signature to SatisfiedPolicy of PolicyHash should have no effect #[test] fn test_satisfied_policy_encode_hash_frivulous_signature() { - let policy = SpendPolicy::Hash(H256::default()); + let policy = SpendPolicy::Hash(Hash256::default()); let satisfied_policy = SatisfiedPolicy { policy, @@ -218,13 +218,13 @@ fn test_satisfied_policy_encode_hash_frivulous_signature() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); assert_eq!(hash, expected); } #[test] fn test_satisfied_policy_encode_hash() { - let policy = SpendPolicy::Hash(H256::default()); + let policy = SpendPolicy::Hash(Hash256::default()); let satisfied_policy = SatisfiedPolicy { policy, @@ -233,7 +233,7 @@ fn test_satisfied_policy_encode_hash() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); assert_eq!(hash, expected); } @@ -258,7 +258,7 @@ fn test_satisfied_policy_encode_unlock_condition_standard() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); + let expected = Hash256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); assert_eq!(hash, expected); } @@ -295,13 +295,13 @@ fn test_satisfied_policy_encode_unlock_condition_complex() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); + let expected = Hash256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); assert_eq!(hash, expected); } #[test] fn test_satisfied_policy_encode_threshold_simple() { - let sub_policy = SpendPolicy::Hash(H256::default()); + let sub_policy = SpendPolicy::Hash(Hash256::default()); let policy = SpendPolicy::Threshold { n: 1, of: vec![sub_policy], @@ -314,7 +314,7 @@ fn test_satisfied_policy_encode_threshold_simple() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); + let expected = Hash256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); assert_eq!(hash, expected); } @@ -329,7 +329,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_success() { ) .unwrap(); - let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -342,7 +342,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_success() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); + let expected = Hash256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); assert_eq!(hash, expected); } @@ -357,7 +357,7 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { ) .unwrap(); - let secret_hash = H256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = spend_policy_atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -370,13 +370,13 @@ fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = H256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); + let expected = Hash256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); assert_eq!(hash, expected); } #[test] fn test_siacoin_input_encode_v2() { - let sub_policy = SpendPolicy::Hash(H256::default()); + let sub_policy = SpendPolicy::Hash(Hash256::default()); let policy = SpendPolicy::Threshold { n: 1, of: vec![sub_policy], @@ -391,9 +391,9 @@ fn test_siacoin_input_encode_v2() { let vin = SiacoinInputV2 { parent: SiacoinElement { state_element: StateElement { - id: H256::default(), + id: Hash256::default(), leaf_index: 0, - merkle_proof: Some(vec![H256::default()]), + merkle_proof: Some(vec![Hash256::default()]), }, siacoin_output: SiacoinOutput { value: 1.into(), @@ -405,7 +405,7 @@ fn test_siacoin_input_encode_v2() { }; let hash = Encoder::encode_and_hash(&vin); - let expected = H256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); + let expected = Hash256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); assert_eq!(hash, expected); } @@ -426,7 +426,7 @@ fn test_attestation_encode() { }; let hash = Encoder::encode_and_hash(&attestation); - let expected = H256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); + let expected = Hash256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); assert_eq!(hash, expected); } @@ -460,7 +460,7 @@ fn test_file_contract_v2_encode() { let file_contract_v2 = V2FileContract { filesize: 1, - file_merkle_root: H256::default(), + file_merkle_root: Hash256::default(), proof_height: 1, expiration_height: 1, renter_output: vout0, @@ -475,7 +475,7 @@ fn test_file_contract_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = H256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); + let expected = Hash256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); assert_eq!(hash, expected); } @@ -509,7 +509,7 @@ fn test_file_contract_element_v2_encode() { let file_contract_v2 = V2FileContract { filesize: 1, - file_merkle_root: H256::default(), + file_merkle_root: Hash256::default(), proof_height: 1, expiration_height: 1, renter_output: vout0, @@ -524,11 +524,11 @@ fn test_file_contract_element_v2_encode() { }; let state_element = StateElement { - id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -538,7 +538,7 @@ fn test_file_contract_element_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = H256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); + let expected = Hash256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); assert_eq!(hash, expected); } @@ -572,7 +572,7 @@ fn test_file_contract_revision_v2_encode() { let file_contract_v2 = V2FileContract { filesize: 1, - file_merkle_root: H256::default(), + file_merkle_root: Hash256::default(), proof_height: 1, expiration_height: 1, renter_output: vout0, @@ -587,11 +587,11 @@ fn test_file_contract_revision_v2_encode() { }; let state_element = StateElement { - id: H256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - H256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - H256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -606,7 +606,7 @@ fn test_file_contract_revision_v2_encode() { }; let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = H256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); + let expected = Hash256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); assert_eq!(hash, expected); } @@ -666,7 +666,7 @@ fn test_v2_transaction_sig_hash() { let tx = serde_json::from_value::(j).unwrap(); let hash = tx.input_sig_hash(); - let expected = H256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); + let expected = Hash256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); assert_eq!(hash, expected); } diff --git a/src/transaction.rs b/src/transaction.rs index 5574ae8..64975d6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,6 +1,6 @@ use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, PrefixedSignature, ScoidH256}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, ChainIndex, H256}; +use crate::types::{Address, ChainIndex, Hash256}; use crate::{Keypair, PublicKey, Signature}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -177,10 +177,10 @@ impl Encodable for SatisfiedPolicy { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StateElement { - pub id: H256, + pub id: Hash256, pub leaf_index: u64, #[serde(default)] - pub merkle_proof: Option>, + pub merkle_proof: Option>, } impl Encodable for StateElement { @@ -258,7 +258,7 @@ impl Encodable for SiafundInputV2 { pub struct SiacoinInputV1 { #[serde(rename = "parentID")] #[serde_as(as = "FromInto")] - pub parent_id: H256, + pub parent_id: Hash256, #[serde(rename = "unlockConditions")] pub unlock_condition: UnlockCondition, } @@ -362,7 +362,7 @@ pub struct CoveredFields { #[serde(rename_all = "camelCase")] pub struct TransactionSignature { #[serde(rename = "parentID")] - pub parent_id: H256, + pub parent_id: Hash256, #[serde(default)] pub public_key_index: u64, #[serde(default)] @@ -405,13 +405,13 @@ impl<'de> Deserialize<'de> for V1Signature { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct FileContract { pub filesize: u64, - pub file_merkle_root: H256, + pub file_merkle_root: Hash256, pub window_start: u64, pub window_end: u64, pub payout: Currency, pub valid_proof_outputs: Vec, pub missed_proof_outputs: Vec, - pub unlock_hash: H256, + pub unlock_hash: Hash256, pub revision_number: u64, } @@ -440,7 +440,7 @@ impl Encodable for FileContract { #[serde(rename_all = "camelCase")] pub struct V2FileContract { pub filesize: u64, - pub file_merkle_root: H256, + pub file_merkle_root: Hash256, pub proof_height: u64, pub expiration_height: u64, pub renter_output: SiacoinOutput, @@ -544,7 +544,7 @@ impl Encodable for Attestation { pub struct StorageProof { pub parent_id: FileContractID, pub leaf: HexArray64, - pub proof: Vec, + pub proof: Vec, } impl Encodable for StorageProof { @@ -558,8 +558,8 @@ impl Encodable for StorageProof { } } -type SiafundOutputID = H256; -type FileContractID = H256; +type SiafundOutputID = Hash256; +type FileContractID = Hash256; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct FileContractRevision { @@ -764,7 +764,7 @@ impl Encodable for V2FileContractRenewal { pub struct V2StorageProof { proof_index: ChainIndexElement, leaf: HexArray64, - proof: Vec, + proof: Vec, } impl V2StorageProof { @@ -820,13 +820,13 @@ pub struct FileContractElementV1 { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FileContractV1 { pub filesize: u64, - pub file_merkle_root: H256, + pub file_merkle_root: Hash256, pub window_start: u64, pub window_end: u64, pub payout: Currency, pub valid_proof_outputs: Vec, pub missed_proof_outputs: Vec, - pub unlock_hash: H256, + pub unlock_hash: Hash256, pub revision_number: u64, } @@ -866,7 +866,7 @@ pub struct V1Transaction { } impl V1Transaction { - pub fn txid(&self) -> H256 { Encoder::encode_and_hash(&V1TransactionSansSigs(self.clone())) } + pub fn txid(&self) -> Hash256 { Encoder::encode_and_hash(&V1TransactionSansSigs(self.clone())) } } impl Encodable for SiafundInputV1 { @@ -952,7 +952,7 @@ impl V2Transaction { } } - pub fn input_sig_hash(&self) -> H256 { + pub fn input_sig_hash(&self) -> Hash256 { let mut encoder = Encoder::default(); encoder.write_distinguisher("sig/input"); encoder.write_u8(V2_REPLAY_PREFIX); @@ -960,7 +960,7 @@ impl V2Transaction { encoder.hash() } - pub fn txid(&self) -> H256 { + pub fn txid(&self) -> Hash256 { let mut encoder = Encoder::default(); encoder.write_distinguisher("id/transaction"); self.encode(&mut encoder); @@ -1199,7 +1199,7 @@ impl V2TransactionBuilder { self } - pub fn input_sig_hash(&self) -> H256 { + pub fn input_sig_hash(&self) -> Hash256 { let mut encoder = Encoder::default(); encoder.write_distinguisher("sig/input"); encoder.write_u8(V2_REPLAY_PREFIX); diff --git a/src/types.rs b/src/types.rs index 6e278ac..df5d82e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -16,7 +16,7 @@ use std::fmt; use std::str::FromStr; mod hash; -pub use hash::H256; +pub use hash::Hash256; const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; @@ -24,7 +24,7 @@ const ADDRESS_CHECKSUM_LENGTH: usize = 6; // TODO this could probably include the checksum within the data type // generating the checksum on the fly is how Sia Go does this however #[derive(Debug, Clone, PartialEq)] -pub struct Address(pub H256); +pub struct Address(pub Hash256); impl Serialize for Address { fn serialize(&self, serializer: S) -> Result @@ -123,7 +123,7 @@ impl FromStr for Address { return Err(ParseAddressError::InvalidChecksum); } - Ok(Address(H256(address_bytes))) + Ok(Address(Hash256(address_bytes))) } } @@ -140,14 +140,14 @@ pub fn v1_standard_address_from_pubkey(pubkey: &PublicKey) -> Address { } #[derive(Clone, Debug, PartialEq)] -pub struct BlockID(pub H256); +pub struct BlockID(pub Hash256); -impl From for H256 { +impl From for Hash256 { fn from(sia_hash: BlockID) -> Self { sia_hash.0 } } -impl From for BlockID { - fn from(h256: H256) -> Self { BlockID(h256) } +impl From for BlockID { + fn from(h256: Hash256) -> Self { BlockID(h256) } } impl<'de> Deserialize<'de> for BlockID { @@ -169,7 +169,7 @@ impl<'de> Deserialize<'de> for BlockID { E: serde::de::Error, { if let Some(hex_str) = value.strip_prefix("bid:") { - H256::from_str_no_prefix(hex_str) + Hash256::from_str_no_prefix(hex_str) .map(BlockID) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { @@ -205,7 +205,7 @@ pub struct ChainIndex { impl Encodable for ChainIndex { fn encode(&self, encoder: &mut Encoder) { encoder.write_u64(self.height); - let block_id: H256 = self.id.clone().into(); + let block_id: Hash256 = self.id.clone().into(); block_id.encode(encoder); } } @@ -246,7 +246,7 @@ pub enum EventType { #[derive(Clone, Debug, Serialize)] pub struct Event { - pub id: H256, + pub id: Hash256, pub index: ChainIndex, pub timestamp: DateTime, #[serde(rename = "maturityHeight")] @@ -265,7 +265,7 @@ impl<'de> Deserialize<'de> for Event { { #[derive(Deserialize, Debug)] struct EventHelper { - id: H256, + id: Hash256, index: ChainIndex, timestamp: DateTime, #[serde(rename = "maturityHeight")] diff --git a/src/types/hash.rs b/src/types/hash.rs index 7e7aadb..b07e217 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -14,10 +14,10 @@ pub enum ParseHashError { InvalidLength(String), } #[derive(Clone, Copy, PartialEq)] -pub struct H256(pub [u8; 32]); +pub struct Hash256(pub [u8; 32]); -impl H256 { - pub const fn const_default() -> H256 { H256([0; 32]) } +impl Hash256 { + pub const fn const_default() -> Hash256 { Hash256([0; 32]) } // Method for parsing a hex string without the "h:" prefix pub fn from_str_no_prefix(hex_str: &str) -> Result { @@ -27,25 +27,25 @@ impl H256 { let mut bytes = [0u8; 32]; match hex::decode_to_slice(hex_str, &mut bytes) { - Ok(_) => Ok(H256(bytes)), + Ok(_) => Ok(Hash256(bytes)), Err(_) => Err(ParseHashError::InvalidHex(hex_str.to_string())), } } } -impl Default for H256 { - fn default() -> Self { H256::const_default() } +impl Default for Hash256 { + fn default() -> Self { Hash256::const_default() } } -impl fmt::Display for H256 { +impl fmt::Display for Hash256 { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "h:{:02x}", self) } } -impl fmt::Debug for H256 { +impl fmt::Debug for Hash256 { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } } -impl fmt::LowerHex for H256 { +impl fmt::LowerHex for Hash256 { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { for byte in &self.0 { write!(f, "{:02x}", byte)?; @@ -54,7 +54,7 @@ impl fmt::LowerHex for H256 { } } -impl Serialize for H256 { +impl Serialize for Hash256 { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -63,7 +63,7 @@ impl Serialize for H256 { } } -impl<'de> Deserialize<'de> for H256 { +impl<'de> Deserialize<'de> for Hash256 { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -71,7 +71,7 @@ impl<'de> Deserialize<'de> for H256 { struct H256Visitor; impl<'de> serde::de::Visitor<'de> for H256Visitor { - type Value = H256; + type Value = Hash256; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string prefixed with 'h:' and followed by a 32 byte hex string") @@ -81,7 +81,7 @@ impl<'de> Deserialize<'de> for H256 { where E: serde::de::Error, { - H256::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) + Hash256::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } } @@ -89,23 +89,23 @@ impl<'de> Deserialize<'de> for H256 { } } -impl FromStr for H256 { +impl FromStr for Hash256 { type Err = ParseHashError; fn from_str(value: &str) -> Result { if let Some(hex_str) = value.strip_prefix("h:") { - H256::from_str_no_prefix(hex_str) + Hash256::from_str_no_prefix(hex_str) } else { Err(ParseHashError::InvalidPrefix(value.to_string())) } } } -impl TryFrom<&str> for H256 { +impl TryFrom<&str> for Hash256 { type Error = ParseHashError; fn try_from(value: &str) -> Result { - H256::from_str(value) + Hash256::from_str(value) } } @@ -117,13 +117,13 @@ mod tests { cross_target_test! { fn test_placeholder_fixme() { let str_reversed = "h:00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; - match H256::from_str(str_reversed) { + match Hash256::from_str(str_reversed) { Ok(reversed_hash) => assert_eq!(format!("{:?}", reversed_hash), str_reversed), _ => panic!("unexpected"), } let str_reversed = "XXXYYY"; - if H256::from_str(str_reversed).is_ok() { + if Hash256::from_str(str_reversed).is_ok() { panic!("unexpected"); } } From 29ae68d0f843e2012d8f4e8e961ef2fa0349d17f Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 00:54:20 -0400 Subject: [PATCH 024/233] change cross_target_test macro to process multiple unit tests --- src/macros.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index f03365a..fcda278 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,15 +1,21 @@ -#[cfg(target_arch = "wasm32")] -use wasm_bindgen_test::wasm_bindgen_test; +// Run a block of unit tests on both wasm32 and non-wasm32 targets +#[cfg(test)] +macro_rules! cross_target_tests { + ($($test_fn:item)*) => { + #[cfg(all(test, target_arch = "wasm32"))] + use wasm_bindgen_test::*; -// Run a unit test on both wasm32 and non-wasm32 targets -macro_rules! cross_target_test { - ($test:item) => { - #[cfg(target_arch = "wasm32")] - #[wasm_bindgen_test] - $test + #[cfg(all(test, target_arch = "wasm32"))] + wasm_bindgen_test_configure!(run_in_browser); - #[cfg(not(target_arch = "wasm32"))] - #[test] - $test + $( + #[cfg(all(test, target_arch = "wasm32"))] + #[wasm_bindgen_test] + $test_fn + + #[cfg(not(target_arch = "wasm32"))] + #[test] + $test_fn + )* }; } \ No newline at end of file From 5491252d14042c86ea2b7d5538f0b9853140498b Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 00:54:43 -0400 Subject: [PATCH 025/233] add FIXME comment to Cargo.toml re: missing info fields --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 0439eed..d849222 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,8 @@ name = "sia-rust" version = "0.1.0" edition = "2018" +# FIXME Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended + [lib] crate-type = ["cdylib", "rlib"] From 573a011d7eee935de8eb6e84113bd109ebbe0a16 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 00:55:06 -0400 Subject: [PATCH 026/233] fix wasm client error unit test --- src/http/client/wasm.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/http/client/wasm.rs b/src/http/client/wasm.rs index b24582a..8eb9272 100644 --- a/src/http/client/wasm.rs +++ b/src/http/client/wasm.rs @@ -135,7 +135,7 @@ mod wasm_tests { }); #[wasm_bindgen_test] - async fn test_sia_wasm_client_wip() { + async fn test_sia_wasm_client_client_error() { use crate::http::endpoints::TxpoolBroadcastRequest; use crate::transaction::V2Transaction; let client = Client::new(CONF.clone()).await.unwrap(); @@ -191,6 +191,12 @@ mod wasm_tests { transactions: vec![], v2transactions: vec![tx], }; - let resp = client.dispatcher(req).await.unwrap(); + match client.dispatcher(req).await.expect_err("Expected HTTP 400 error") { + ApiClientError::UnexpectedHttpStatus { status: StatusCode::BAD_REQUEST, body } => { + () + }, + e => panic!("Unexpected error: {:?}", e), + } + } } From 4431d456cd26a671ab733aadeac10d69484d9805 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 01:03:54 -0400 Subject: [PATCH 027/233] cargo fmt --- src/blake2b_internal.rs | 2 +- src/http/client.rs | 8 +++----- src/http/client/native.rs | 10 ++++------ src/http/client/wasm.rs | 19 ++++++++----------- src/http/client/wasm/wasm_fetch.rs | 24 ++++++++---------------- src/macros.rs | 2 +- src/spend_policy.rs | 7 ++++++- src/types.rs | 4 ++-- 8 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 2f486f5..d360d91 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -6,7 +6,7 @@ use blake2b_simd::Params; use std::default::Default; #[cfg(test)] use hex; -#[cfg(test)] use std::convert::{TryInto, TryFrom}; +#[cfg(test)] use std::convert::{TryFrom, TryInto}; const LEAF_HASH_PREFIX: [u8; 1] = [0u8]; const NODE_HASH_PREFIX: [u8; 1] = [1u8]; diff --git a/src/http/client.rs b/src/http/client.rs index cbf6a7c..f7da538 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -64,13 +64,11 @@ pub enum ApiClientError { #[error("UrlParse error: {0}")] UrlParse(#[from] url::ParseError), #[error("UnexpectedHttpStatus error: status:{status} body:{body}")] - UnexpectedHttpStatus{ status: http::StatusCode, body: String }, + UnexpectedHttpStatus { status: http::StatusCode, body: String }, #[error("Serde error: {0}")] Serde(#[from] serde_json::Error), #[error("UnexpectedEmptyResponse error: {expected_type}")] - UnexpectedEmptyResponse { - expected_type: String, - }, + UnexpectedEmptyResponse { expected_type: String }, #[error("WasmFetchError error: {0}")] #[cfg(target_arch = "wasm32")] WasmFetchError(#[from] FetchError), @@ -186,4 +184,4 @@ impl EndpointSchema { Ok(url) } -} \ No newline at end of file +} diff --git a/src/http/client/native.rs b/src/http/client/native.rs index b7469ab..8e5cf33 100644 --- a/src/http/client/native.rs +++ b/src/http/client/native.rs @@ -63,7 +63,8 @@ impl ApiClient for NativeClient { ClientBody::Utf8(body) => self.client.request(schema.method.into(), url).body(body).build(), ClientBody::Json(body) => self.client.request(schema.method.into(), url).json(&body).build(), ClientBody::Bytes(body) => self.client.request(schema.method.into(), url).body(body).build(), - }.map_err(ApiClientError::ReqwestError)?; + } + .map_err(ApiClientError::ReqwestError)?; Ok(req) } @@ -105,11 +106,8 @@ impl ApiClient for NativeClient { .map_err(|e| format!("Failed to retrieve body: {}", e)) .unwrap_or_else(|e| e); - Err(ApiClientError::UnexpectedHttpStatus { - status, - body, - }) - } + Err(ApiClientError::UnexpectedHttpStatus { status, body }) + }, } } } diff --git a/src/http/client/wasm.rs b/src/http/client/wasm.rs index 8eb9272..ddd4422 100644 --- a/src/http/client/wasm.rs +++ b/src/http/client/wasm.rs @@ -102,12 +102,9 @@ impl ApiClient for Client { .body .map(|b| format!("{}", b)) // Use Display trait to format Body .unwrap_or_else(|| "".to_string()); // If body is None, use an empty string - - Err(ApiClientError::UnexpectedHttpStatus { - status, - body, - }) - } + + Err(ApiClientError::UnexpectedHttpStatus { status, body }) + }, } } } @@ -122,10 +119,10 @@ impl ApiClientHelpers for Client {} mod wasm_tests { use super::*; use blake2b_simd::Hash; + use log::info; use once_cell::sync::Lazy; use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; - use log::info; wasm_bindgen_test_configure!(run_in_browser); @@ -192,11 +189,11 @@ mod wasm_tests { v2transactions: vec![tx], }; match client.dispatcher(req).await.expect_err("Expected HTTP 400 error") { - ApiClientError::UnexpectedHttpStatus { status: StatusCode::BAD_REQUEST, body } => { - () - }, + ApiClientError::UnexpectedHttpStatus { + status: StatusCode::BAD_REQUEST, + body, + } => (), e => panic!("Unexpected error: {:?}", e), } - } } diff --git a/src/http/client/wasm/wasm_fetch.rs b/src/http/client/wasm/wasm_fetch.rs index 0e7a5ce..5040d96 100644 --- a/src/http/client/wasm/wasm_fetch.rs +++ b/src/http/client/wasm/wasm_fetch.rs @@ -28,26 +28,20 @@ pub fn stringify_js_error(error: &JsValue) -> String { #[derive(Debug, Error)] pub enum FetchError { #[error("Error deserializing '{uri}' response: {error}")] - ErrorDeserializing { - uri: String, - error: String, - }, - + ErrorDeserializing { uri: String, error: String }, + #[error("Transport '{uri}' error: {error}")] - Transport { - uri: String, - error: String, - }, - + Transport { uri: String, error: String }, + #[error("Invalid status code in response")] InvalidStatusCode(#[from] http::status::InvalidStatusCode), - + #[error("Invalid headers in response: {0}")] InvalidHeadersInResponse(String), - + #[error("Invalid body: {0}")] InvalidBody(String), - + #[error("Internal error: {0}")] Internal(String), } @@ -295,8 +289,6 @@ mod tests { let err = JsRequest::new_with_str_and_init(&uri, &req_init) .map_err(|e| FetchError::Internal(stringify_js_error(&e))) .unwrap_err(); - assert!(err - .to_string() - .contains("is an url with embedded credentials")); + assert!(err.to_string().contains("is an url with embedded credentials")); } } diff --git a/src/macros.rs b/src/macros.rs index fcda278..ac888a8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -18,4 +18,4 @@ macro_rules! cross_target_tests { $test_fn )* }; -} \ No newline at end of file +} diff --git a/src/spend_policy.rs b/src/spend_policy.rs index 7bdd6e4..f310262 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -238,7 +238,12 @@ pub fn spend_policy_atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64 } } -pub fn spend_policy_atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { +pub fn spend_policy_atomic_swap_success( + alice: PublicKey, + bob: PublicKey, + lock_time: u64, + hash: Hash256, +) -> SpendPolicy { match spend_policy_atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[1] = of[1].opacify(); diff --git a/src/types.rs b/src/types.rs index df5d82e..cab37eb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,9 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; - // FIXME expose these types or the entire module +// FIXME expose these types or the entire module +pub use crate::transaction::Currency; // FIXME should be imported via the transaction module use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; -pub use crate::transaction::Currency; // FIXME should be imported via the transaction module use crate::PublicKey; use blake2b_simd::Params; use chrono::{DateTime, Utc}; From 5660b45474b5d8c842130d779290feb3dec4b95b Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:10:55 -0400 Subject: [PATCH 028/233] remove unneccesary pub export --- src/types/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index b07e217..f0603d5 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -17,7 +17,7 @@ pub enum ParseHashError { pub struct Hash256(pub [u8; 32]); impl Hash256 { - pub const fn const_default() -> Hash256 { Hash256([0; 32]) } + const fn const_default() -> Hash256 { Hash256([0; 32]) } // Method for parsing a hex string without the "h:" prefix pub fn from_str_no_prefix(hex_str: &str) -> Result { From 9f3ccfb7819c86c949394df840c83c5eec3f8161 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:11:21 -0400 Subject: [PATCH 029/233] remove unnecessary trait derives --- src/types/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index f0603d5..b3b8bf3 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use std::fmt; use thiserror::Error; -#[derive(Debug, Deserialize, Serialize, Error)] +#[derive(Debug, Error)] pub enum ParseHashError { #[error("expected hex string prefixed with 'h:', found {0}")] InvalidPrefix(String), From 6e4b762de69f1e191e44b40a1fca8c39fa18242f Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:11:39 -0400 Subject: [PATCH 030/233] add doc comment for cross_target_tests macro --- src/macros.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index ac888a8..0298d92 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,5 @@ -// Run a block of unit tests on both wasm32 and non-wasm32 targets +/// Run a block of unit tests on both wasm32 and non-wasm32 targets +/// Can only be used once per scope due to wasm_bindgen initialization #[cfg(test)] macro_rules! cross_target_tests { ($($test_fn:item)*) => { From f1b3eeb7f58848707f8f71a62201e7e252d0adfd Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:12:17 -0400 Subject: [PATCH 031/233] add hash unit tests; cargo fmt --- src/types/hash.rs | 133 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 116 insertions(+), 17 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index b3b8bf3..9eb5604 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -1,7 +1,7 @@ -use serde::{Deserialize, Serialize, Deserializer, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::TryFrom; -use std::str::FromStr; use std::fmt; +use std::str::FromStr; use thiserror::Error; #[derive(Debug, Error)] @@ -104,28 +104,127 @@ impl FromStr for Hash256 { impl TryFrom<&str> for Hash256 { type Error = ParseHashError; - fn try_from(value: &str) -> Result { - Hash256::from_str(value) - } + fn try_from(value: &str) -> Result { Hash256::from_str(value) } } #[cfg(test)] mod tests { use super::*; - use std::str::FromStr; - - cross_target_test! { - fn test_placeholder_fixme() { - let str_reversed = "h:00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"; - match Hash256::from_str(str_reversed) { - Ok(reversed_hash) => assert_eq!(format!("{:?}", reversed_hash), str_reversed), - _ => panic!("unexpected"), + use serde_json; + + cross_target_tests! { + fn test_default() { + let hash = Hash256::try_from("h:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + assert_eq!(hash, Hash256::default()); + } + + fn test_valid() { + let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + } + + fn test_display() { + let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + } + + fn test_debug() { + let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(format!("{:?}", hash), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + } + + fn test_serialize() { + let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let serialized = serde_json::to_string(&hash).unwrap(); + assert_eq!(&serialized, r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); + } + + fn test_deserialize() { + let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let deserialized: Hash256 = serde_json::from_str(r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); + assert_eq!(deserialized, hash); + } + + fn test_deserialize_missing_prefix() { + let err = serde_json::from_str::(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).expect_err("no prefix"); + format!("{:?}", err).contains("expected a string prefixed with 'h:' and followed by a 32 byte hex string"); + } + + fn test_missing_prefix() { + let test_case = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + let err = Hash256::try_from(test_case).expect_err("no prefix"); + match err { + ParseHashError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), } + } - let str_reversed = "XXXYYY"; - if Hash256::from_str(str_reversed).is_ok() { - panic!("unexpected"); + fn test_corrupt_prefix() { + let test_case = ":c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + let err = Hash256::try_from(test_case).expect_err("no prefix"); + match err { + ParseHashError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_wrong_prefix() { + let test_case = "i:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + let err = Hash256::try_from(test_case).expect_err("wrong prefix"); + match err { + ParseHashError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_invalid_hex() { + let err = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); + let expected = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg"; + match err { + ParseHashError::InvalidHex(ref e) if expected == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_invalid_length() { + let err = Hash256::try_from("h:badc0de").expect_err("invalid length"); + let expected = "badc0de"; + match err { + ParseHashError::InvalidLength(ref e) if expected == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_valid() { + let hash = Hash256::from_str_no_prefix("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + assert_eq!(hash, Hash256::default()) + } + + fn test_from_str_no_prefix_invalid_length() { + let err = Hash256::from_str_no_prefix("badc0de").expect_err("invalid length"); + let expected = "badc0de"; + match err { + ParseHashError::InvalidLength(ref e) if expected == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_invalid_hex() { + let err = Hash256::from_str_no_prefix("g00000000000000000000000000000000000000000000000000000000000000e").expect_err("invalid hex"); + let expected = "g00000000000000000000000000000000000000000000000000000000000000e"; + match err { + ParseHashError::InvalidHex(ref e) if expected == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_invalid_has_prefix() { + let err = Hash256::from_str_no_prefix("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").expect_err("invalid hex"); + let expected = "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + match err { + ParseHashError::InvalidLength(ref e) if expected == e => (), + _ => panic!("unexpected error: {:?}", err), } } } -} +} \ No newline at end of file From cc556e0ee7f0c4d7e3f249ca550015676802859a Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:12:32 -0400 Subject: [PATCH 032/233] cargo fmt --- src/types/hash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index 9eb5604..47430b6 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -199,7 +199,7 @@ mod tests { let hash = Hash256::from_str_no_prefix("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()) } - + fn test_from_str_no_prefix_invalid_length() { let err = Hash256::from_str_no_prefix("badc0de").expect_err("invalid length"); let expected = "badc0de"; @@ -227,4 +227,4 @@ mod tests { } } } -} \ No newline at end of file +} From 1812f110354a009d37fc3925a42756c50c8b6d06 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 02:17:22 -0400 Subject: [PATCH 033/233] derive Eq for Hash256 --- src/types/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index 47430b6..be08bae 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -13,7 +13,7 @@ pub enum ParseHashError { #[error("expected 32 byte hex string, found {0}")] InvalidLength(String), } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq)] pub struct Hash256(pub [u8; 32]); impl Hash256 { From 7dda6d41b99a3ab89471de12c720ec79dd9f53a6 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 03:28:40 -0400 Subject: [PATCH 034/233] remove PrefixedSignature type; move Signature type to own module --- src/lib.rs | 52 ++--------------------- src/types.rs | 3 ++ src/types/signature.rs | 94 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 src/types/signature.rs diff --git a/src/lib.rs b/src/lib.rs index 199bd5b..e977a87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,10 @@ mod macros; use derive_more::Display; -use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, - Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, Signer}; +use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer}; use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; -use std::str::FromStr; pub mod blake2b_internal; pub mod encoding; @@ -17,6 +15,8 @@ pub mod spend_policy; pub mod transaction; pub mod types; +use types::{Signature, SignatureError}; // FIXME remove this when we move Keypair + #[derive(Debug, Display)] pub enum KeypairError { InvalidSecretKey(Ed25519SignatureError), @@ -37,51 +37,7 @@ impl Keypair { Ok(Keypair(Ed25519Keypair { secret, public })) } - pub fn sign(&self, message: &[u8]) -> Signature { self.0.sign(message).into() } -} - -#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct Signature(pub Ed25519Signature); - -impl Signature { - pub fn from_bytes(bytes: &[u8]) -> Result { - let signature = Ed25519Signature::from_bytes(bytes).map_err(SignatureError::ParseError)?; - Ok(Signature(signature)) - } -} - -impl From for Signature { - fn from(signature: Ed25519Signature) -> Self { Signature(signature) } -} - -impl Deref for Signature { - type Target = Ed25519Signature; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -#[derive(Debug, Display)] -pub enum SignatureError { - ParseError(ed25519_dalek::ed25519::Error), - InvalidSignature(Ed25519SignatureError), -} - -impl From for SignatureError { - fn from(e: Ed25519SignatureError) -> Self { SignatureError::InvalidSignature(e) } -} - -impl fmt::LowerHex for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.0.to_bytes())) } -} - -impl FromStr for Signature { - type Err = SignatureError; - - fn from_str(s: &str) -> Result { - Ed25519Signature::from_str(s) - .map(Signature) - .map_err(SignatureError::InvalidSignature) - } + pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } } #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] diff --git a/src/types.rs b/src/types.rs index cab37eb..dff0101 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,6 +18,9 @@ use std::str::FromStr; mod hash; pub use hash::Hash256; +mod signature; +pub use signature::{Signature, SignatureError}; // FIXME only export Signature + const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; diff --git a/src/types/signature.rs b/src/types/signature.rs new file mode 100644 index 0000000..51c4f76 --- /dev/null +++ b/src/types/signature.rs @@ -0,0 +1,94 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::str::FromStr; +use std::fmt; +use thiserror::Error; +use ed25519_dalek::Signature as Ed25519Signature; + +#[derive(Debug, Error)] +pub enum SignatureError { + #[error("expected 64 byte hex string prefixed with 'sig:', found {0}")] + Parse(#[from] ed25519_dalek::ed25519::Error), + #[error("expected 64 byte hex string prefixed with 'sig:',, found {0}")] + InvalidPrefix(String), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Signature(Ed25519Signature); + +impl Signature { + pub fn new(signature: Ed25519Signature) -> Self { Signature(signature) } + + pub fn from_bytes(bytes: &[u8]) -> Result { + Ed25519Signature::from_bytes(bytes) + .map(Signature) + .map_err(SignatureError::Parse) + } + + pub fn to_bytes(&self) -> [u8; 64] { self.0.to_bytes() } + + // Method for parsing a hex string without the "sig:" prefix + pub fn from_str_no_prefix(hex_str: &str) -> Result { + Ed25519Signature::from_str(hex_str) + .map(Signature) + .map_err(SignatureError::Parse) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SignatureVisitor; + + impl<'de> serde::de::Visitor<'de> for SignatureVisitor { + type Value = Signature; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a 64 byte hex string representing a ed25519 signature prefixed with 'sig:'") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Signature::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } + } + + deserializer.deserialize_str(SignatureVisitor) + } +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}", self)) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "sig:{:x}", self.0) } +} + +impl fmt::LowerHex for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Delegate to the fmt::LowerHex implementation of the inner Ed25519Signature + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl FromStr for Signature { + type Err = SignatureError; + + fn from_str(value: &str) -> Result { + if let Some(hex_str) = value.strip_prefix("sig:") { + Signature::from_str_no_prefix(hex_str) + } else { + Err(SignatureError::InvalidPrefix(value.to_string())) + } + } +} + From eb561e29cb430ec9288a8341655a42edef07c189 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 03:30:20 -0400 Subject: [PATCH 035/233] Remove PrefixedSignature entirely; remove related serde_as remnants --- src/encoding.rs | 57 +--------------------------------------------- src/transaction.rs | 12 ++-------- 2 files changed, 3 insertions(+), 66 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index fe4a368..cbb56d3 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,6 +1,6 @@ use crate::blake2b_internal::hash_blake2b_single; use crate::types::Hash256; -use crate::{PublicKey, Signature}; +use crate::PublicKey; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::From; use std::convert::{TryFrom, TryInto}; @@ -81,62 +81,7 @@ pub trait Encodable { fn encode(&self, encoder: &mut Encoder); } -/// This wrapper allows us to use Signature internally but still serde as "sig:" prefixed string -#[derive(Debug)] -pub struct PrefixedSignature(pub Signature); -impl<'de> Deserialize<'de> for PrefixedSignature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct PrefixedSignatureVisitor; - - impl<'de> serde::de::Visitor<'de> for PrefixedSignatureVisitor { - type Value = PrefixedSignature; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'sig:' and followed by a 128-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("sig:") { - Signature::from_str(hex_str) - .map(PrefixedSignature) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(PrefixedSignatureVisitor) - } -} - -impl Serialize for PrefixedSignature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{}", self)) - } -} - -impl fmt::Display for PrefixedSignature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "sig:{:x}", self.0) } -} - -impl From for Signature { - fn from(sia_hash: PrefixedSignature) -> Self { sia_hash.0 } -} - -impl From for PrefixedSignature { - fn from(signature: Signature) -> Self { PrefixedSignature(signature) } -} /// This wrapper allows us to use PublicKey internally but still serde as "ed25519:" prefixed string #[derive(Clone, Debug, PartialEq)] diff --git a/src/transaction.rs b/src/transaction.rs index 64975d6..b779b85 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, PrefixedSignature, ScoidH256}; +use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, ScoidH256}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256}; use crate::{Keypair, PublicKey, Signature}; @@ -113,7 +113,6 @@ pub type Preimage = Vec; pub struct SatisfiedPolicy { #[serde_as(as = "FromInto")] pub policy: SpendPolicy, - #[serde_as(as = "Vec>")] #[serde(default, skip_serializing_if = "Vec::is_empty")] pub signatures: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -173,7 +172,6 @@ impl Encodable for SatisfiedPolicy { } } -#[serde_as] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StateElement { @@ -357,7 +355,6 @@ pub struct CoveredFields { pub signatures: Vec, } -#[serde_as] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionSignature { @@ -452,9 +449,7 @@ pub struct V2FileContract { #[serde_as(as = "FromInto")] pub host_public_key: PublicKey, pub revision_number: u64, - #[serde_as(as = "FromInto")] pub renter_signature: Signature, - #[serde_as(as = "FromInto")] pub host_signature: Signature, } @@ -539,7 +534,7 @@ impl Encodable for Attestation { self.signature.encode(encoder); } } -#[serde_as] + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct StorageProof { pub parent_id: FileContractID, @@ -718,7 +713,6 @@ impl Encodable for V2FileContractFinalization { fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder); } } -#[serde_as] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct V2FileContractRenewal { @@ -726,9 +720,7 @@ pub struct V2FileContractRenewal { new_contract: V2FileContract, renter_rollover: Currency, host_rollover: Currency, - #[serde_as(as = "FromInto")] renter_signature: Signature, - #[serde_as(as = "FromInto")] host_signature: Signature, } From 1d00d0af9db99ca5f041d765fb67cd2a2de068b0 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 03:30:38 -0400 Subject: [PATCH 036/233] removed derive(Copy) from Signature --- src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction.rs b/src/transaction.rs index b779b85..ea63e8c 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1212,7 +1212,7 @@ impl V2TransactionBuilder { for p in &uc.unlock_keys { match p { UnlockKey::Ed25519(pk) if pk == &keypair.public() => { - si.satisfied_policy.signatures.push(sig) + si.satisfied_policy.signatures.push(sig.clone()) }, _ => (), } From ef872450a70e7bfa4fffb9f55da82d898d90f8c9 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 03:30:54 -0400 Subject: [PATCH 037/233] removed derive(Copy) from Signature again --- src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction.rs b/src/transaction.rs index ea63e8c..fd802d5 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1207,7 +1207,7 @@ impl V2TransactionBuilder { let sig = keypair.sign(&sig_hash.0); for si in &mut self.siacoin_inputs { match &si.satisfied_policy.policy { - SpendPolicy::PublicKey(pk) if pk == &keypair.public() => si.satisfied_policy.signatures.push(sig), + SpendPolicy::PublicKey(pk) if pk == &keypair.public() => si.satisfied_policy.signatures.push(sig.clone()), SpendPolicy::UnlockConditions(uc) => { for p in &uc.unlock_keys { match p { From ea3a14a0e5ea6a15e0dc75f8af8dbd22e4cebad2 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 03:31:16 -0400 Subject: [PATCH 038/233] fix Signature import paths --- src/spend_policy.rs | 4 ++-- src/transaction.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/spend_policy.rs b/src/spend_policy.rs index f310262..5238bc3 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -2,8 +2,8 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlo use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; use crate::specifier::Specifier; use crate::transaction::{Preimage, SatisfiedPolicy}; -use crate::types::{Address, Hash256}; -use crate::{PublicKey, Signature}; +use crate::types::{Address, Hash256, Signature}; +use crate::PublicKey; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; use nom::combinator::all_consuming; diff --git a/src/transaction.rs b/src/transaction.rs index fd802d5..82001ad 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,7 +1,7 @@ use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, ScoidH256}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, ChainIndex, Hash256}; -use crate::{Keypair, PublicKey, Signature}; +use crate::types::{Address, ChainIndex, Hash256, Signature}; +use crate::{Keypair, PublicKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; From 25279d97e6ed5c3d1543c7c7690c7658a19e7601 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 23:45:23 -0400 Subject: [PATCH 039/233] validate signature r Point --- src/types/signature.rs | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/types/signature.rs b/src/types/signature.rs index 51c4f76..61ceab6 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -2,14 +2,17 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::str::FromStr; use std::fmt; use thiserror::Error; -use ed25519_dalek::Signature as Ed25519Signature; +use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; +use curve25519_dalek::edwards::CompressedEdwardsY; #[derive(Debug, Error)] pub enum SignatureError { - #[error("expected 64 byte hex string prefixed with 'sig:', found {0}")] + #[error("parsing error: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] Parse(#[from] ed25519_dalek::ed25519::Error), - #[error("expected 64 byte hex string prefixed with 'sig:',, found {0}")] + #[error("invalid prefix: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] InvalidPrefix(String), + #[error("corrupt R point: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] + CorruptRPoint(String), } #[derive(Clone, Debug, PartialEq)] @@ -19,18 +22,37 @@ impl Signature { pub fn new(signature: Ed25519Signature) -> Self { Signature(signature) } pub fn from_bytes(bytes: &[u8]) -> Result { - Ed25519Signature::from_bytes(bytes) + let signature = Ed25519Signature::from_bytes(bytes) .map(Signature) - .map_err(SignatureError::Parse) + .map_err(SignatureError::Parse)?; + + match signature.validate_r_point() { + true => Ok(signature), + false => Err(SignatureError::CorruptRPoint(hex::encode(bytes))), + } } - pub fn to_bytes(&self) -> [u8; 64] { self.0.to_bytes() } + pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { self.0.to_bytes() } // Method for parsing a hex string without the "sig:" prefix pub fn from_str_no_prefix(hex_str: &str) -> Result { - Ed25519Signature::from_str(hex_str) + let signature = Ed25519Signature::from_str(hex_str) .map(Signature) - .map_err(SignatureError::Parse) + .map_err(SignatureError::Parse)?; + + match signature.validate_r_point() { + true => Ok(signature), + false => Err(SignatureError::CorruptRPoint(hex_str.to_string())), + } + } + + /// Check if R value is a valid point on the Ed25519 curve + pub fn validate_r_point(&self) -> bool { + let r_bytes = &self.0.to_bytes()[0..SIGNATURE_LENGTH/2]; + + println!("r_bytes len: {}", r_bytes.len()); + // Create a CompressedEdwardsY point from the first 32 bytes + CompressedEdwardsY::from_slice(r_bytes).decompress().is_some() } } From 299e3236de571fef3f85a6cc5466b41a717fc639 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 23:45:35 -0400 Subject: [PATCH 040/233] signature unit tests --- src/types/signature.rs | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/types/signature.rs b/src/types/signature.rs index 61ceab6..c59ceff 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -114,3 +114,125 @@ impl FromStr for Signature { } } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + const VALID_STR: &str = "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + const VALID_JSON_STR: &str = r#""sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#; + + fn valid_signature() -> Signature { + Signature::from_str(VALID_STR).unwrap() + } + + cross_target_tests! { + fn test_display() { + assert_eq!(valid_signature().to_string(), VALID_STR); + } + + fn test_debug() { + assert_eq!(format!("{:?}", valid_signature()), "Signature(ed25519::Signature(F43380794A6384E3D24D9908143C05DD37AAAC8959EFB65D986FEB70FE289A5E26B84E0AC712AF01A2F85F8727DA18AAE13A599A51FB066D098591E40CB26902))"); + } + + fn test_serialize() { + assert_eq!(&serde_json::to_string(&valid_signature()).unwrap(), VALID_JSON_STR); + } + + fn test_deserialize() { + assert_eq!(serde_json::from_str::(VALID_JSON_STR).unwrap(), valid_signature()); + } + + fn test_deserialize_missing_prefix() { + let err = serde_json::from_str::(r#""f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#).expect_err("no prefix"); + let mystr = format!("{:?}", err); + assert!(mystr.contains("expected a 64 byte hex string representing a ed25519 signature prefixed with 'sig:'")); + } + + fn test_missing_prefix() { + let test_case = "f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_corrupt_prefix() { + let test_case = ":f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_wrong_prefix() { + let test_case = "dig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::InvalidPrefix(ref e) if test_case == e => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_invalid_hex() { + let test_case = "sig:g43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::Parse(_) => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_invalid_r_signature() { + let test_case = "sig:00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::CorruptRPoint(_) => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_invalid_length() { + let test_case = "sig:badc0de"; + let err = Signature::from_str(test_case).expect_err("no prefix"); + match err { + SignatureError::Parse(e) => println!("{:?}", e), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_valid() { + let sig = Signature::from_str_no_prefix("f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902").unwrap(); + assert_eq!(sig, valid_signature()) + } + + fn test_from_str_no_prefix_invalid_length() { + let test_case = "badc0de"; + let err = Signature::from_str_no_prefix(test_case).expect_err("invalid length"); + match err { + SignatureError::Parse(_) => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_invalid_hex() { + let test_case = "g43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let err = Signature::from_str_no_prefix(test_case).expect_err("invalid hex"); + match err { + SignatureError::Parse(_) => (), + _ => panic!("unexpected error: {:?}", err), + } + } + + fn test_from_str_no_prefix_invalid_has_prefix() { + let err = Signature::from_str_no_prefix(VALID_STR).expect_err("invalid hex"); + match err { + SignatureError::Parse(_) => (), + _ => panic!("unexpected error: {:?}", err), + } + } + } +} From cf70707fc6b408a07472b70d5e68ab5e71066604 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 23:46:06 -0400 Subject: [PATCH 041/233] fix hash unit test --- src/types/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index be08bae..daf6e81 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -147,7 +147,7 @@ mod tests { fn test_deserialize_missing_prefix() { let err = serde_json::from_str::(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).expect_err("no prefix"); - format!("{:?}", err).contains("expected a string prefixed with 'h:' and followed by a 32 byte hex string"); + assert!(format!("{:?}", err).contains("expected a string prefixed with 'h:' and followed by a 32 byte hex string")); } fn test_missing_prefix() { From 5c860d9e9083fd39383641cd737769e9ab704a96 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 23:46:33 -0400 Subject: [PATCH 042/233] import curve25519-dalek; already in dep tree --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 103674a..5b7b332 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1312,6 +1312,7 @@ dependencies = [ "base64 0.21.7", "blake2b_simd", "chrono", + "curve25519-dalek", "derive_more", "ed25519-dalek", "futures", diff --git a/Cargo.toml b/Cargo.toml index d849222..076c6b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] ed25519-dalek = { version = "1.0.1", features = ["serde"] } +curve25519-dalek = "3.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1", features = ["preserve_order", "raw_value"] } serde_with = "1.14.0" From 85063684b8f8acf8f83fd458e9e8ed850487d5a8 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 21 Sep 2024 23:46:49 -0400 Subject: [PATCH 043/233] more verbose ParseHashError --- src/types/hash.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index daf6e81..b8b808c 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -6,11 +6,11 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum ParseHashError { - #[error("expected hex string prefixed with 'h:', found {0}")] + #[error("invalid prefix: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidPrefix(String), - #[error("expected hex string, found {0}")] + #[error("invalid hex: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidHex(String), - #[error("expected 32 byte hex string, found {0}")] + #[error("invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidLength(String), } #[derive(Clone, Copy, Eq, PartialEq)] From 184aa5ea8f77526fddcb27c0b398561bac43d1c4 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 01:49:42 -0400 Subject: [PATCH 044/233] move PublicKey and KeyPair types to module - breaking change for KDF imports --- src/blake2b_internal.rs | 3 +-- src/encoding.rs | 3 +-- src/lib.rs | 59 ----------------------------------------- src/spend_policy.rs | 3 +-- src/transaction.rs | 3 +-- src/types.rs | 8 +++--- src/types/keypair.rs | 58 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 70 deletions(-) create mode 100644 src/types/keypair.rs diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index d360d91..8f0c87e 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -1,7 +1,6 @@ use crate::specifier::Specifier; use crate::spend_policy::UnlockKey; -use crate::types::Hash256; -use crate::PublicKey; +use crate::types::{Hash256, PublicKey}; use blake2b_simd::Params; use std::default::Default; diff --git a/src/encoding.rs b/src/encoding.rs index cbb56d3..9bae1f0 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,6 +1,5 @@ use crate::blake2b_internal::hash_blake2b_single; -use crate::types::Hash256; -use crate::PublicKey; +use crate::types::{Hash256, PublicKey}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::From; use std::convert::{TryFrom, TryInto}; diff --git a/src/lib.rs b/src/lib.rs index e977a87..452da11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,6 @@ #[macro_use] mod macros; -use derive_more::Display; -use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer}; -use serde::{Deserialize, Serialize}; -use std::fmt; -use std::ops::Deref; - pub mod blake2b_internal; pub mod encoding; pub mod http; @@ -15,61 +9,8 @@ pub mod spend_policy; pub mod transaction; pub mod types; -use types::{Signature, SignatureError}; // FIXME remove this when we move Keypair - -#[derive(Debug, Display)] -pub enum KeypairError { - InvalidSecretKey(Ed25519SignatureError), -} - #[cfg(test)] mod tests; #[cfg(test)] #[macro_use] extern crate serde_json; -// FIXME move everything below this to types mod -pub struct Keypair(pub Ed25519Keypair); - -impl Keypair { - pub fn from_private_bytes(bytes: &[u8]) -> Result { - let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; - let public = Ed25519PublicKey::from(&secret); - Ok(Keypair(Ed25519Keypair { secret, public })) - } - - pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } -} - -#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct PublicKey(pub Ed25519PublicKey); - -impl PublicKey { - pub fn from_bytes(bytes: &[u8]) -> Result { - let public_key = Ed25519PublicKey::from_bytes(bytes)?; - Ok(PublicKey(public_key)) - } -} - -impl From for PublicKey { - fn from(public_key: Ed25519PublicKey) -> Self { PublicKey(public_key) } -} - -impl Deref for PublicKey { - type Target = Ed25519PublicKey; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl fmt::Display for PublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.as_bytes())) } -} - -impl Deref for Keypair { - type Target = Ed25519Keypair; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl Keypair { - pub fn public(&self) -> PublicKey { PublicKey(self.0.public) } -} diff --git a/src/spend_policy.rs b/src/spend_policy.rs index 5238bc3..30130f9 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -2,8 +2,7 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlo use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; use crate::specifier::Specifier; use crate::transaction::{Preimage, SatisfiedPolicy}; -use crate::types::{Address, Hash256, Signature}; -use crate::PublicKey; +use crate::types::{Address, Hash256, PublicKey, Signature}; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; use nom::combinator::all_consuming; diff --git a/src/transaction.rs b/src/transaction.rs index 82001ad..982f560 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,7 +1,6 @@ use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, ScoidH256}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, ChainIndex, Hash256, Signature}; -use crate::{Keypair, PublicKey}; +use crate::types::{Address, ChainIndex, Hash256, Signature, Keypair, PublicKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; diff --git a/src/types.rs b/src/types.rs index dff0101..a0687d1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,7 +4,6 @@ use crate::encoding::{Encodable, Encoder}; pub use crate::transaction::Currency; // FIXME should be imported via the transaction module use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; -use crate::PublicKey; use blake2b_simd::Params; use chrono::{DateTime, Utc}; use hex::FromHexError; @@ -16,10 +15,13 @@ use std::fmt; use std::str::FromStr; mod hash; -pub use hash::Hash256; +pub use hash::{Hash256, ParseHashError}; mod signature; -pub use signature::{Signature, SignatureError}; // FIXME only export Signature +pub use signature::{Signature, SignatureError}; + +mod keypair; +pub use keypair::{Keypair, KeypairError, PublicKey}; const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; diff --git a/src/types/keypair.rs b/src/types/keypair.rs new file mode 100644 index 0000000..4fbaead --- /dev/null +++ b/src/types/keypair.rs @@ -0,0 +1,58 @@ +use derive_more::Display; +use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer}; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; + +use crate::types::{Signature, SignatureError}; // FIXME remove this when we move Keypair + +#[derive(Debug, Display)] +pub enum KeypairError { + InvalidSecretKey(Ed25519SignatureError), +} + +pub struct Keypair(pub Ed25519Keypair); + +impl Keypair { + pub fn from_private_bytes(bytes: &[u8]) -> Result { + let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; + let public = Ed25519PublicKey::from(&secret); + Ok(Keypair(Ed25519Keypair { secret, public })) + } + + pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } +} + +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PublicKey(pub Ed25519PublicKey); + +impl PublicKey { + pub fn from_bytes(bytes: &[u8]) -> Result { + let public_key = Ed25519PublicKey::from_bytes(bytes)?; + Ok(PublicKey(public_key)) + } +} + +impl From for PublicKey { + fn from(public_key: Ed25519PublicKey) -> Self { PublicKey(public_key) } +} + +impl Deref for PublicKey { + type Target = Ed25519PublicKey; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.as_bytes())) } +} + +impl Deref for Keypair { + type Target = Ed25519Keypair; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl Keypair { + pub fn public(&self) -> PublicKey { PublicKey(self.0.public) } +} From d067ac3021ed9e0f7e62d4eff6efdb156f640ab0 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 01:54:46 -0400 Subject: [PATCH 045/233] rename ScoidH256 -> SiacoinOutputId --- src/encoding.rs | 20 ++++++++++---------- src/transaction.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 9bae1f0..bed8762 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -139,10 +139,10 @@ impl From for PrefixedPublicKey { /// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string #[derive(Clone, Debug, PartialEq)] -pub struct ScoidH256(pub Hash256); +pub struct SiacoinOutputId(pub Hash256); // FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros -impl<'de> Deserialize<'de> for ScoidH256 { +impl<'de> Deserialize<'de> for SiacoinOutputId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -150,7 +150,7 @@ impl<'de> Deserialize<'de> for ScoidH256 { struct ScoidH256Visitor; impl<'de> serde::de::Visitor<'de> for ScoidH256Visitor { - type Value = ScoidH256; + type Value = SiacoinOutputId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string prefixed with 'scoid:' and followed by a 64-character hex string") @@ -162,7 +162,7 @@ impl<'de> Deserialize<'de> for ScoidH256 { { if let Some(hex_str) = value.strip_prefix("scoid:") { Hash256::from_str(hex_str) - .map(ScoidH256) + .map(SiacoinOutputId) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) @@ -174,7 +174,7 @@ impl<'de> Deserialize<'de> for ScoidH256 { } } -impl Serialize for ScoidH256 { +impl Serialize for SiacoinOutputId { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -183,16 +183,16 @@ impl Serialize for ScoidH256 { } } -impl fmt::Display for ScoidH256 { +impl fmt::Display for SiacoinOutputId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } } -impl From for Hash256 { - fn from(sia_hash: ScoidH256) -> Self { sia_hash.0 } +impl From for Hash256 { + fn from(sia_hash: SiacoinOutputId) -> Self { sia_hash.0 } } -impl From for ScoidH256 { - fn from(h256: Hash256) -> Self { ScoidH256(h256) } +impl From for SiacoinOutputId { + fn from(h256: Hash256) -> Self { SiacoinOutputId(h256) } } impl Encodable for Hash256 { diff --git a/src/transaction.rs b/src/transaction.rs index 982f560..d3f80f7 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, ScoidH256}; +use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, SiacoinOutputId}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Signature, Keypair, PublicKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; From e2a4b194fcbf897f9d98d5d7b6038d219b246067 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:18:23 -0400 Subject: [PATCH 046/233] fix SiacoinOutputID symbol rename --- src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction.rs b/src/transaction.rs index d3f80f7..dd7c5b7 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -254,7 +254,7 @@ impl Encodable for SiafundInputV2 { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SiacoinInputV1 { #[serde(rename = "parentID")] - #[serde_as(as = "FromInto")] + #[serde_as(as = "FromInto")] pub parent_id: Hash256, #[serde(rename = "unlockConditions")] pub unlock_condition: UnlockCondition, From b9e18756e2bee54f1c6dc8aa10d2f58a12d851a8 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:19:11 -0400 Subject: [PATCH 047/233] fix typo --- src/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction.rs b/src/transaction.rs index dd7c5b7..2a2a6e0 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -254,7 +254,7 @@ impl Encodable for SiafundInputV2 { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SiacoinInputV1 { #[serde(rename = "parentID")] - #[serde_as(as = "FromInto")] + #[serde_as(as = "FromInto")] pub parent_id: Hash256, #[serde(rename = "unlockConditions")] pub unlock_condition: UnlockCondition, From 01448a6151c416e82e646fbdb818e66bb4aab704 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:23:05 -0400 Subject: [PATCH 048/233] fix various imports in tests --- src/tests/encoding.rs | 3 +-- src/tests/spend_policy.rs | 3 +-- src/tests/transaction.rs | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 5288142..e65a6de 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -1,8 +1,7 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::Encoder; use crate::spend_policy::{SpendPolicy, UnlockCondition}; -use crate::types::{Address, Hash256}; -use crate::PublicKey; +use crate::types::{Address, Hash256, PublicKey}; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index bff2c50..6f5b39a 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,6 +1,5 @@ use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, Hash256}; -use crate::PublicKey; +use crate::types::{Address, Hash256, PublicKey}; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index b462d26..d4d8fb2 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -4,8 +4,7 @@ use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_s use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, StateElement, V2FileContract, V2FileContractElement, V2Transaction}; -use crate::types::{v1_standard_address_from_pubkey, Address, Hash256}; -use crate::{PublicKey, Signature}; +use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, PublicKey, Keypair, Signature}; use std::convert::TryFrom; use std::str::FromStr; @@ -672,7 +671,6 @@ fn test_v2_transaction_sig_hash() { #[test] fn test_v2_transaction_signing() { - use crate::{Keypair, Signature}; let j = json!( { "siacoinInputs": [ From 39697f4cf4daaab6109d5badfbe069a4713e6e86 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:35:52 -0400 Subject: [PATCH 049/233] move SiacoinOutputID to transaction.rs; remove serde_as hack, use type directly --- src/encoding.rs | 58 ----------------------------------- src/tests/transaction.rs | 4 +-- src/transaction.rs | 66 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 65 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index bed8762..a09ed27 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -137,64 +137,6 @@ impl From for PrefixedPublicKey { fn from(public_key: PublicKey) -> Self { PrefixedPublicKey(public_key) } } -/// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string -#[derive(Clone, Debug, PartialEq)] -pub struct SiacoinOutputId(pub Hash256); - -// FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros -impl<'de> Deserialize<'de> for SiacoinOutputId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct ScoidH256Visitor; - - impl<'de> serde::de::Visitor<'de> for ScoidH256Visitor { - type Value = SiacoinOutputId; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'scoid:' and followed by a 64-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("scoid:") { - Hash256::from_str(hex_str) - .map(SiacoinOutputId) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(ScoidH256Visitor) - } -} - -impl Serialize for SiacoinOutputId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{}", self)) - } -} - -impl fmt::Display for SiacoinOutputId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } -} - -impl From for Hash256 { - fn from(sia_hash: SiacoinOutputId) -> Self { sia_hash.0 } -} - -impl From for SiacoinOutputId { - fn from(h256: Hash256) -> Self { SiacoinOutputId(h256) } -} - impl Encodable for Hash256 { fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0); } } diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index d4d8fb2..5aa4e4d 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -2,7 +2,7 @@ use crate::encoding::Encoder; use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition}; use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, - SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, + SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputId, SiacoinOutputVersion, StateElement, V2FileContract, V2FileContractElement, V2Transaction}; use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, PublicKey, Keypair, Signature}; use std::convert::TryFrom; @@ -17,7 +17,7 @@ fn test_siacoin_input_encode() { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + parent_id: SiacoinOutputId(Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap()), unlock_condition, }; diff --git a/src/transaction.rs b/src/transaction.rs index 2a2a6e0..55b375b 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey, SiacoinOutputId}; +use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Signature, Keypair, PublicKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; @@ -250,19 +250,17 @@ impl Encodable for SiafundInputV2 { } // https://github.com/SiaFoundation/core/blob/6c19657baf738c6b730625288e9b5413f77aa659/types/types.go#L197-L198 -#[serde_as] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SiacoinInputV1 { #[serde(rename = "parentID")] - #[serde_as(as = "FromInto")] - pub parent_id: Hash256, + pub parent_id: SiacoinOutputId, #[serde(rename = "unlockConditions")] pub unlock_condition: UnlockCondition, } impl Encodable for SiacoinInputV1 { fn encode(&self, encoder: &mut Encoder) { - self.parent_id.encode(encoder); + self.parent_id.0.encode(encoder); self.unlock_condition.encode(encoder); } } @@ -331,6 +329,64 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { } } +/// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string +#[derive(Clone, Debug, PartialEq)] +pub struct SiacoinOutputId(pub Hash256); + +// FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros +impl<'de> Deserialize<'de> for SiacoinOutputId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ScoidH256Visitor; + + impl<'de> serde::de::Visitor<'de> for ScoidH256Visitor { + type Value = SiacoinOutputId; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string prefixed with 'scoid:' and followed by a 64-character hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if let Some(hex_str) = value.strip_prefix("scoid:") { + Hash256::from_str_no_prefix(hex_str) + .map(SiacoinOutputId) + .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } else { + Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } + } + } + + deserializer.deserialize_str(ScoidH256Visitor) + } +} + +impl Serialize for SiacoinOutputId { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}", self)) + } +} + +impl fmt::Display for SiacoinOutputId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } +} + +impl From for Hash256 { + fn from(sia_hash: SiacoinOutputId) -> Self { sia_hash.0 } +} + +impl From for SiacoinOutputId { + fn from(h256: Hash256) -> Self { SiacoinOutputId(h256) } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct SiacoinOutput { pub value: Currency, From f12b273596ab42371960266736a567ec28b1ba74 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:55:54 -0400 Subject: [PATCH 050/233] remove unused import --- src/encoding.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/encoding.rs b/src/encoding.rs index a09ed27..fa6ae93 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -4,7 +4,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::From; use std::convert::{TryFrom, TryInto}; use std::fmt; -use std::str::FromStr; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] From 8e936c20bd84cfd6e9f5e92c32804364133d2bd7 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:56:38 -0400 Subject: [PATCH 051/233] remove comment no longer applicable; rename symbol --- src/transaction.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 55b375b..e3a4b52 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -329,7 +329,6 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { } } -/// This wrapper allows us to use H256 internally but still serde as "scoid:" prefixed string #[derive(Clone, Debug, PartialEq)] pub struct SiacoinOutputId(pub Hash256); @@ -339,9 +338,9 @@ impl<'de> Deserialize<'de> for SiacoinOutputId { where D: Deserializer<'de>, { - struct ScoidH256Visitor; + struct SiacoinOutputIdVisitor; - impl<'de> serde::de::Visitor<'de> for ScoidH256Visitor { + impl<'de> serde::de::Visitor<'de> for SiacoinOutputIdVisitor { type Value = SiacoinOutputId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -362,7 +361,7 @@ impl<'de> Deserialize<'de> for SiacoinOutputId { } } - deserializer.deserialize_str(ScoidH256Visitor) + deserializer.deserialize_str(SiacoinOutputIdVisitor) } } @@ -376,7 +375,7 @@ impl Serialize for SiacoinOutputId { } impl fmt::Display for SiacoinOutputId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h:{}", self.0) } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "scoid:{:02x}", self.0) } } impl From for Hash256 { From 68c21d0880f15fc78dd13ad8869669e87dc76f32 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 02:58:07 -0400 Subject: [PATCH 052/233] fix tests; remove unused import --- src/tests/transaction.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 5aa4e4d..f9b063c 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -2,7 +2,7 @@ use crate::encoding::Encoder; use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition}; use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, - SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputId, SiacoinOutputVersion, + SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, StateElement, V2FileContract, V2FileContractElement, V2Transaction}; use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, PublicKey, Keypair, Signature}; use std::convert::TryFrom; @@ -17,7 +17,7 @@ fn test_siacoin_input_encode() { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: SiacoinOutputId(Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap()), + parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap().into(), unlock_condition, }; @@ -147,7 +147,7 @@ fn test_state_element_encode_null_merkle_proof() { #[test] fn test_siacoin_input_encode_v1() { let vin = SiacoinInputV1 { - parent_id: Hash256::default(), + parent_id: Hash256::default().into(), unlock_condition: UnlockCondition::new(vec![], 0, 0), }; From e3cafcdc5bdbb65d123d3764fb5e174858506128 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 03:05:40 -0400 Subject: [PATCH 053/233] remove unused trait impl --- src/encoding.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index fa6ae93..3f31083 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -9,9 +9,6 @@ use std::fmt; #[serde(try_from = "String", into = "String")] pub struct HexArray64(#[serde(with = "hex")] pub [u8; 64]); -impl AsRef<[u8]> for HexArray64 { - fn as_ref(&self) -> &[u8] { &self.0 } -} impl TryFrom for HexArray64 { type Error = hex::FromHexError; From c845c541092b0586630bcee12848799ffa724f0f Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 03:10:31 -0400 Subject: [PATCH 054/233] rename symbol HexArray64->Leaf --- src/encoding.rs | 10 +++++----- src/transaction.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 3f31083..478350b 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -7,21 +7,21 @@ use std::fmt; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] -pub struct HexArray64(#[serde(with = "hex")] pub [u8; 64]); +pub struct Leaf(#[serde(with = "hex")] pub [u8; 64]); -impl TryFrom for HexArray64 { +impl TryFrom for Leaf { type Error = hex::FromHexError; fn try_from(value: String) -> Result { let bytes = hex::decode(value)?; let array = bytes.try_into().map_err(|_| hex::FromHexError::InvalidStringLength)?; - Ok(HexArray64(array)) + Ok(Leaf(array)) } } -impl From for String { - fn from(value: HexArray64) -> Self { hex::encode(value.0) } +impl From for String { + fn from(value: Leaf) -> Self { hex::encode(value.0) } } // https://github.com/SiaFoundation/core/blob/092850cc52d3d981b19c66cd327b5d945b3c18d3/types/encoding.go#L16 diff --git a/src/transaction.rs b/src/transaction.rs index e3a4b52..9ebc5f9 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, HexArray64, PrefixedPublicKey}; +use crate::encoding::{Encodable, Encoder, Leaf, PrefixedPublicKey}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Signature, Keypair, PublicKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; @@ -592,7 +592,7 @@ impl Encodable for Attestation { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct StorageProof { pub parent_id: FileContractID, - pub leaf: HexArray64, + pub leaf: Leaf, pub proof: Vec, } @@ -809,7 +809,7 @@ impl Encodable for V2FileContractRenewal { #[serde(rename_all = "camelCase")] pub struct V2StorageProof { proof_index: ChainIndexElement, - leaf: HexArray64, + leaf: Leaf, proof: Vec, } From f1921cb85f489ae7726dea9c2571c30195d8db91 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 03:11:27 -0400 Subject: [PATCH 055/233] cargo fmt --- src/encoding.rs | 3 --- src/lib.rs | 1 - src/tests/transaction.rs | 6 ++++-- src/transaction.rs | 6 ++++-- src/types/keypair.rs | 3 ++- src/types/signature.rs | 13 +++++-------- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 478350b..644b932 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -9,7 +9,6 @@ use std::fmt; #[serde(try_from = "String", into = "String")] pub struct Leaf(#[serde(with = "hex")] pub [u8; 64]); - impl TryFrom for Leaf { type Error = hex::FromHexError; @@ -76,8 +75,6 @@ pub trait Encodable { fn encode(&self, encoder: &mut Encoder); } - - /// This wrapper allows us to use PublicKey internally but still serde as "ed25519:" prefixed string #[derive(Clone, Debug, PartialEq)] pub struct PrefixedPublicKey(pub PublicKey); diff --git a/src/lib.rs b/src/lib.rs index 452da11..712c7cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,4 +13,3 @@ pub mod types; #[cfg(test)] #[macro_use] extern crate serde_json; - diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index f9b063c..7f85ff9 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -4,7 +4,7 @@ use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_s use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, StateElement, V2FileContract, V2FileContractElement, V2Transaction}; -use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, PublicKey, Keypair, Signature}; +use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, Keypair, PublicKey, Signature}; use std::convert::TryFrom; use std::str::FromStr; @@ -17,7 +17,9 @@ fn test_siacoin_input_encode() { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap().into(), + parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000") + .unwrap() + .into(), unlock_condition, }; diff --git a/src/transaction.rs b/src/transaction.rs index 9ebc5f9..4e7d7e3 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,6 +1,6 @@ use crate::encoding::{Encodable, Encoder, Leaf, PrefixedPublicKey}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; -use crate::types::{Address, ChainIndex, Hash256, Signature, Keypair, PublicKey}; +use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; @@ -1261,7 +1261,9 @@ impl V2TransactionBuilder { let sig = keypair.sign(&sig_hash.0); for si in &mut self.siacoin_inputs { match &si.satisfied_policy.policy { - SpendPolicy::PublicKey(pk) if pk == &keypair.public() => si.satisfied_policy.signatures.push(sig.clone()), + SpendPolicy::PublicKey(pk) if pk == &keypair.public() => { + si.satisfied_policy.signatures.push(sig.clone()) + }, SpendPolicy::UnlockConditions(uc) => { for p in &uc.unlock_keys { match p { diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 4fbaead..63faf70 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -1,5 +1,6 @@ use derive_more::Display; -use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer}; +use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, + SignatureError as Ed25519SignatureError, Signer}; use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; diff --git a/src/types/signature.rs b/src/types/signature.rs index c59ceff..01382a8 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -1,9 +1,9 @@ +use curve25519_dalek::edwards::CompressedEdwardsY; +use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::str::FromStr; use std::fmt; +use std::str::FromStr; use thiserror::Error; -use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; -use curve25519_dalek::edwards::CompressedEdwardsY; #[derive(Debug, Error)] pub enum SignatureError { @@ -48,7 +48,7 @@ impl Signature { /// Check if R value is a valid point on the Ed25519 curve pub fn validate_r_point(&self) -> bool { - let r_bytes = &self.0.to_bytes()[0..SIGNATURE_LENGTH/2]; + let r_bytes = &self.0.to_bytes()[0..SIGNATURE_LENGTH / 2]; println!("r_bytes len: {}", r_bytes.len()); // Create a CompressedEdwardsY point from the first 32 bytes @@ -114,7 +114,6 @@ impl FromStr for Signature { } } - #[cfg(test)] mod tests { use super::*; @@ -123,9 +122,7 @@ mod tests { const VALID_STR: &str = "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; const VALID_JSON_STR: &str = r#""sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#; - fn valid_signature() -> Signature { - Signature::from_str(VALID_STR).unwrap() - } + fn valid_signature() -> Signature { Signature::from_str(VALID_STR).unwrap() } cross_target_tests! { fn test_display() { From ddcd0356148d8b90edf8a3115460c4b7ce975f81 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 04:14:26 -0400 Subject: [PATCH 056/233] remove Copy impl from Hash256 --- src/types/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index b8b808c..57b08a1 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -13,7 +13,7 @@ pub enum ParseHashError { #[error("invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidLength(String), } -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct Hash256(pub [u8; 32]); impl Hash256 { From e5f0240cbdfa7ebef76c17cd28fef6be32d8cd25 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 04:15:00 -0400 Subject: [PATCH 057/233] fix errors caused by remove of Hash256 Copy; add FIXME comment --- src/blake2b_internal.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 8f0c87e..3431d1e 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -23,6 +23,10 @@ const STANDARD_SIGS_REQUIRED_BLAKE2B_HASH: [u8; 32] = [ 0x5a, 0xb4, 0xe1, 0xa7, 0x6e, 0x62, 0x4a, 0x87, 0x98, 0xcb, 0x63, 0x49, 0x7b, ]; +// FIXME remove direct indexing of arrays or add sanity checks to prevent out of bound access + +/// Directly ported from Sia core +/// https://github.com/SiaFoundation/core/blob/0f61e58ab7ea932da7e9f710c592d595406356c6/internal/blake2b/blake2b.go#L66 #[derive(Debug, PartialEq)] pub struct Accumulator { trees: [Hash256; 64], @@ -31,8 +35,9 @@ pub struct Accumulator { impl Default for Accumulator { fn default() -> Self { + // Initialize all bytes to zero Accumulator { - trees: [Hash256::default(); 64], // Initialize all bytes to zero + trees: std::array::from_fn(|_| Hash256::default()), num_leaves: 0, } } @@ -61,7 +66,7 @@ impl Accumulator { if i == 64 { return Hash256::default(); // Return all zeros if no leaves } - let mut root = self.trees[i as usize]; + let mut root = self.trees[i as usize].clone(); for j in i + 1..64 { if self.has_tree_at_height(j) { root = hash_blake2b_pair(&NODE_HASH_PREFIX, &self.trees[j as usize].0, &root.0); @@ -153,7 +158,7 @@ fn test_accumulator_new() { let default_accumulator = Accumulator::default(); let expected = Accumulator { - trees: [Hash256::default(); 64], + trees: std::array::from_fn(|_| Hash256::default()), num_leaves: 0, }; assert_eq!(default_accumulator, expected) From 999d1ff6a96ce351b09164dbe69044eee507ac6c Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 04:26:05 -0400 Subject: [PATCH 058/233] move Leaf to transaction module --- src/encoding.rs | 19 ------------------- src/transaction.rs | 21 ++++++++++++++++++++- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 644b932..f2d3260 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,27 +2,8 @@ use crate::blake2b_internal::hash_blake2b_single; use crate::types::{Hash256, PublicKey}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::From; -use std::convert::{TryFrom, TryInto}; use std::fmt; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(try_from = "String", into = "String")] -pub struct Leaf(#[serde(with = "hex")] pub [u8; 64]); - -impl TryFrom for Leaf { - type Error = hex::FromHexError; - - fn try_from(value: String) -> Result { - let bytes = hex::decode(value)?; - let array = bytes.try_into().map_err(|_| hex::FromHexError::InvalidStringLength)?; - Ok(Leaf(array)) - } -} - -impl From for String { - fn from(value: Leaf) -> Self { hex::encode(value.0) } -} - // https://github.com/SiaFoundation/core/blob/092850cc52d3d981b19c66cd327b5d945b3c18d3/types/encoding.go#L16 // TODO go implementation limits this to 1024 bytes, should we? #[derive(Default)] diff --git a/src/transaction.rs b/src/transaction.rs index 4e7d7e3..f10c0d0 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,10 +1,11 @@ -use crate::encoding::{Encodable, Encoder, Leaf, PrefixedPublicKey}; +use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use serde_with::{serde_as, FromInto}; +use std::convert::{TryFrom, TryInto}; use std::fmt; use std::ops::Deref; use std::str::FromStr; @@ -589,6 +590,24 @@ impl Encodable for Attestation { } } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct Leaf(#[serde(with = "hex")] pub [u8; 64]); + +impl TryFrom for Leaf { + type Error = hex::FromHexError; + + fn try_from(value: String) -> Result { + let bytes = hex::decode(value)?; + let array = bytes.try_into().map_err(|_| hex::FromHexError::InvalidStringLength)?; + Ok(Leaf(array)) + } +} + +impl From for String { + fn from(value: Leaf) -> Self { hex::encode(value.0) } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct StorageProof { pub parent_id: FileContractID, From 8afa970677775a370b879499cd0efc4f3067eb38 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 04:26:20 -0400 Subject: [PATCH 059/233] wrap tests in mod --- src/encoding.rs | 166 +++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 80 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index f2d3260..26073b3 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -115,90 +115,96 @@ impl Encodable for Hash256 { fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0); } } -#[test] -fn test_encoder_default_hash() { - assert_eq!( - Encoder::default().hash(), - Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() - ) -} +#[cfg(test)] +mod tests { + use super::*; + use std::convert::TryFrom; + + #[test] + fn test_encoder_default_hash() { + assert_eq!( + Encoder::default().hash(), + Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + ) + } -#[test] -fn test_encoder_write_bytes() { - let mut encoder = Encoder::default(); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() - ); -} + #[test] + fn test_encoder_write_bytes() { + let mut encoder = Encoder::default(); + encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() + ); + } -#[test] -fn test_encoder_write_u8() { - let mut encoder = Encoder::default(); - encoder.write_u8(1); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); -} + #[test] + fn test_encoder_write_u8() { + let mut encoder = Encoder::default(); + encoder.write_u8(1); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + } -#[test] -fn test_encoder_write_u64() { - let mut encoder = Encoder::default(); - encoder.write_u64(1); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() - ); -} + #[test] + fn test_encoder_write_u64() { + let mut encoder = Encoder::default(); + encoder.write_u64(1); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() + ); + } -#[test] -fn test_encoder_write_distiguisher() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() - ); -} + #[test] + fn test_encoder_write_distiguisher() { + let mut encoder = Encoder::default(); + encoder.write_distinguisher("test"); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() + ); + } -#[test] -fn test_encoder_write_bool() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); -} + #[test] + fn test_encoder_write_bool() { + let mut encoder = Encoder::default(); + encoder.write_bool(true); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + } -#[test] -fn test_encoder_reset() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); - - encoder.reset(); - encoder.write_bool(false); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() - ); -} + #[test] + fn test_encoder_reset() { + let mut encoder = Encoder::default(); + encoder.write_bool(true); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + + encoder.reset(); + encoder.write_bool(false); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() + ); + } -#[test] -fn test_encoder_complex() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - encoder.write_bool(true); - encoder.write_u8(1); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() - ); -} + #[test] + fn test_encoder_complex() { + let mut encoder = Encoder::default(); + encoder.write_distinguisher("test"); + encoder.write_bool(true); + encoder.write_u8(1); + encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() + ); + } +} \ No newline at end of file From 7b635dddb7fc325220ca4a744e0c2875ddb6c19c Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 04:54:43 -0400 Subject: [PATCH 060/233] remove PublicKey Deref impl --- src/types/keypair.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 63faf70..140456e 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -32,18 +32,16 @@ impl PublicKey { let public_key = Ed25519PublicKey::from_bytes(bytes)?; Ok(PublicKey(public_key)) } + + pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes() } + + pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } } impl From for PublicKey { fn from(public_key: Ed25519PublicKey) -> Self { PublicKey(public_key) } } -impl Deref for PublicKey { - type Target = Ed25519PublicKey; - - fn deref(&self) -> &Self::Target { &self.0 } -} - impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.as_bytes())) } } From dddc302e0600e82048695751de9f0fc1bcb2a8b9 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 05:51:16 -0400 Subject: [PATCH 061/233] remove PrefixedPublicKey type --- src/encoding.rs | 60 +-------------------------------------------- src/spend_policy.rs | 9 ++++--- src/transaction.rs | 5 +--- 3 files changed, 7 insertions(+), 67 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 26073b3..3b3cb11 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,8 +1,5 @@ use crate::blake2b_internal::hash_blake2b_single; -use crate::types::{Hash256, PublicKey}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::convert::From; -use std::fmt; +use crate::types::Hash256; // https://github.com/SiaFoundation/core/blob/092850cc52d3d981b19c66cd327b5d945b3c18d3/types/encoding.go#L16 // TODO go implementation limits this to 1024 bytes, should we? @@ -56,61 +53,6 @@ pub trait Encodable { fn encode(&self, encoder: &mut Encoder); } -/// This wrapper allows us to use PublicKey internally but still serde as "ed25519:" prefixed string -#[derive(Clone, Debug, PartialEq)] -pub struct PrefixedPublicKey(pub PublicKey); - -impl<'de> Deserialize<'de> for PrefixedPublicKey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct PrefixedPublicKeyVisitor; - - impl<'de> serde::de::Visitor<'de> for PrefixedPublicKeyVisitor { - type Value = PrefixedPublicKey; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'ed25519:' and followed by a 64-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("ed25519:") { - let bytes = - hex::decode(hex_str).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self))?; - PublicKey::from_bytes(&bytes) - .map(PrefixedPublicKey) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(PrefixedPublicKeyVisitor) - } -} - -impl Serialize for PrefixedPublicKey { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("ed25519:{}", hex::encode(self.0.as_bytes()))) - } -} - -impl From for PublicKey { - fn from(sia_public_key: PrefixedPublicKey) -> Self { sia_public_key.0 } -} - -impl From for PrefixedPublicKey { - fn from(public_key: PublicKey) -> Self { PrefixedPublicKey(public_key) } -} - impl Encodable for Hash256 { fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0); } } diff --git a/src/spend_policy.rs b/src/spend_policy.rs index 30130f9..a80de67 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -1,5 +1,5 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, Accumulator}; -use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; +use crate::encoding::{Encodable, Encoder}; use crate::specifier::Specifier; use crate::transaction::{Preimage, SatisfiedPolicy}; use crate::types::{Address, Hash256, PublicKey, Signature}; @@ -25,13 +25,14 @@ pub enum SpendPolicy { UnlockConditions(UnlockCondition), // For v1 compatibility } +// FIXME this can now be fully removed now that Prefixed* types are removed /// Helper to serialize/deserialize SpendPolicy with prefixed PublicKey and H256 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(tag = "type", content = "policy", rename_all = "camelCase")] pub enum SpendPolicyHelper { Above(u64), After(u64), - Pk(PrefixedPublicKey), + Pk(PublicKey), H(Hash256), Thresh { n: u8, of: Vec }, Opaque(Address), @@ -43,7 +44,7 @@ impl From for SpendPolicy { match helper { SpendPolicyHelper::Above(height) => SpendPolicy::Above(height), SpendPolicyHelper::After(time) => SpendPolicy::After(time), - SpendPolicyHelper::Pk(pk) => SpendPolicy::PublicKey(pk.0), + SpendPolicyHelper::Pk(pk) => SpendPolicy::PublicKey(pk), SpendPolicyHelper::H(hash) => SpendPolicy::Hash(hash), SpendPolicyHelper::Thresh { n, of } => SpendPolicy::Threshold { n, @@ -60,7 +61,7 @@ impl From for SpendPolicyHelper { match policy { SpendPolicy::Above(height) => SpendPolicyHelper::Above(height), SpendPolicy::After(time) => SpendPolicyHelper::After(time), - SpendPolicy::PublicKey(pk) => SpendPolicyHelper::Pk(PrefixedPublicKey(pk)), + SpendPolicy::PublicKey(pk) => SpendPolicyHelper::Pk(pk), SpendPolicy::Hash(hash) => SpendPolicyHelper::H(hash), SpendPolicy::Threshold { n, of } => SpendPolicyHelper::Thresh { n, diff --git a/src/transaction.rs b/src/transaction.rs index f10c0d0..eb1088a 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::encoding::{Encodable, Encoder, PrefixedPublicKey}; +use crate::encoding::{Encodable, Encoder}; use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; @@ -487,7 +487,6 @@ impl Encodable for FileContract { } } -#[serde_as] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct V2FileContract { @@ -499,9 +498,7 @@ pub struct V2FileContract { pub host_output: SiacoinOutput, pub missed_host_value: Currency, pub total_collateral: Currency, - #[serde_as(as = "FromInto")] pub renter_public_key: PublicKey, - #[serde_as(as = "FromInto")] pub host_public_key: PublicKey, pub revision_number: u64, pub renter_signature: Signature, From b2ee198f65796804a3447ff876d67c879f242220 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 05:52:49 -0400 Subject: [PATCH 062/233] impl required PublicKey traits; better KeypairError handling --- src/types/keypair.rs | 103 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 140456e..4a26a1c 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -1,15 +1,24 @@ -use derive_more::Display; +use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; -use std::ops::Deref; +use thiserror::Error; -use crate::types::{Signature, SignatureError}; // FIXME remove this when we move Keypair +use crate::types::Signature; -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum KeypairError { + #[error("invalid secret key: {0}")] InvalidSecretKey(Ed25519SignatureError), + #[error("invalid public key length: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] + PublicKeyInvalidLength(String), + #[error("public key invalid hex: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] + PublicKeyInvalidHex(String), + #[error("public key invalid: corrupt curve point {0}")] + PublicKeyCorruptPoint(String), + #[error("public key invalid: from_bytes failed {0}")] + PublicKeyParseBytes(Ed25519SignatureError), } pub struct Keypair(pub Ed25519Keypair); @@ -24,18 +33,84 @@ impl Keypair { pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } } -#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct PublicKey(pub Ed25519PublicKey); impl PublicKey { - pub fn from_bytes(bytes: &[u8]) -> Result { - let public_key = Ed25519PublicKey::from_bytes(bytes)?; - Ok(PublicKey(public_key)) + pub fn from_bytes(bytes: &[u8]) -> Result { + let public_key = Ed25519PublicKey::from_bytes(bytes) + .map(PublicKey) + .map_err(KeypairError::PublicKeyParseBytes)?; + + match public_key.validate_point() { + true => Ok(public_key), + false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), + } + } + + /// Check if public key is a valid point on the Ed25519 curve + pub fn validate_point(&self) -> bool { + // Create a CompressedEdwardsY point from the first 32 bytes + CompressedEdwardsY::from_slice(&self.0.to_bytes()).decompress().is_some() } pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes() } pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } + + // Method for parsing a hex string without the "ed25519:" prefix + pub fn from_str_no_prefix(hex_str: &str) -> Result { + let mut bytes = [0u8; 32]; + hex::decode_to_slice(hex_str, &mut bytes) + .map_err(|_| KeypairError::PublicKeyInvalidHex(hex_str.to_string()))?; + + let public_key = Self::from_bytes(&bytes)?; + + match public_key.validate_point() { + true => Ok(public_key), + false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), + } + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PublicKeyVisitor; + + impl<'de> serde::de::Visitor<'de> for PublicKeyVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string prefixed with 'ed25519:' and followed by a 64-character hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if let Some(hex_str) = value.strip_prefix("ed25519:") { + PublicKey::from_str_no_prefix(hex_str) + .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } else { + Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } + } + } + + deserializer.deserialize_str(PublicKeyVisitor) + } +} + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}", self)) + } } impl From for PublicKey { @@ -43,13 +118,13 @@ impl From for PublicKey { } impl fmt::Display for PublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.as_bytes())) } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ed25519:{:02x}", self) } } -impl Deref for Keypair { - type Target = Ed25519Keypair; - - fn deref(&self) -> &Self::Target { &self.0 } +impl fmt::LowerHex for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", hex::encode(self.as_bytes())) + } } impl Keypair { From a18a634283c49ee818cf8a22f6b6bfbc0388c4ac Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 05:53:45 -0400 Subject: [PATCH 063/233] remove unused impl --- src/types/keypair.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 4a26a1c..35f953d 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -113,10 +113,6 @@ impl Serialize for PublicKey { } } -impl From for PublicKey { - fn from(public_key: Ed25519PublicKey) -> Self { PublicKey(public_key) } -} - impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ed25519:{:02x}", self) } } From 24e258fe48bdf9a8eb73661105517eef642b907f Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 05:57:41 -0400 Subject: [PATCH 064/233] remove Copy impl from PublicKey --- src/blake2b_internal.rs | 2 +- src/tests/encoding.rs | 3 +-- src/transaction.rs | 2 +- src/types/keypair.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 3431d1e..af91740 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -122,7 +122,7 @@ pub fn timelock_leaf(timelock: u64) -> Hash256 { // ┌─────┴─────┐ │ // timelock pubkey sigsrequired pub fn standard_unlock_hash(pubkey: &PublicKey) -> Hash256 { - let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(*pubkey)); + let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey.clone())); let timelock_pubkey_node = hash_blake2b_pair(&NODE_HASH_PREFIX, &STANDARD_TIMELOCK_BLAKE2B_HASH, &pubkey_leaf.0); hash_blake2b_pair( &NODE_HASH_PREFIX, diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index e65a6de..0698d79 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -171,12 +171,11 @@ fn test_unlock_condition_unlock_hash_standard() { &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); + let unlock_condition = UnlockCondition::new(vec![pubkey.clone()], 0, 1); let hash = unlock_condition.unlock_hash(); let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected); - let hash = standard_unlock_hash(&pubkey); assert_eq!(hash, expected); } diff --git a/src/transaction.rs b/src/transaction.rs index eb1088a..182883d 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -159,7 +159,7 @@ impl Encodable for SatisfiedPolicy { SpendPolicy::UnlockConditions(uc) => { for unlock_key in &uc.unlock_keys { if let UnlockKey::Ed25519(public_key) = unlock_key { - rec(&SpendPolicy::PublicKey(*public_key), encoder, sigi, prei, sp); + rec(&SpendPolicy::PublicKey(public_key.clone()), encoder, sigi, prei, sp); } // else FIXME consider when this is possible, is it always developer error or could it be forced maliciously? } diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 35f953d..b075c73 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -33,7 +33,7 @@ impl Keypair { pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct PublicKey(pub Ed25519PublicKey); impl PublicKey { From 6555c1ab7ff3816c0af855abf00e4d8d25ddd207 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 08:13:41 -0400 Subject: [PATCH 065/233] remove unused PublicKey method --- src/types/keypair.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index b075c73..59e7148 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -122,7 +122,3 @@ impl fmt::LowerHex for PublicKey { write!(f, "{}", hex::encode(self.as_bytes())) } } - -impl Keypair { - pub fn public(&self) -> PublicKey { PublicKey(self.0.public) } -} From d01b2cf7b9870fee4027358202b1453eb4b4c35d Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 08:14:00 -0400 Subject: [PATCH 066/233] remove .public() method from PublicKey --- src/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 182883d..bbba00a 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1277,13 +1277,13 @@ impl V2TransactionBuilder { let sig = keypair.sign(&sig_hash.0); for si in &mut self.siacoin_inputs { match &si.satisfied_policy.policy { - SpendPolicy::PublicKey(pk) if pk == &keypair.public() => { + SpendPolicy::PublicKey(pk) if pk == &keypair.public => { si.satisfied_policy.signatures.push(sig.clone()) }, SpendPolicy::UnlockConditions(uc) => { for p in &uc.unlock_keys { match p { - UnlockKey::Ed25519(pk) if pk == &keypair.public() => { + UnlockKey::Ed25519(pk) if pk == &keypair.public => { si.satisfied_policy.signatures.push(sig.clone()) }, _ => (), From aa42baa28df07d8c0299c4813bf7af63aa7d88ec Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 08:14:58 -0400 Subject: [PATCH 067/233] refactor Keypair type; impl Verifier, Signer --- src/types/keypair.rs | 42 ++++++++++++++++++++++++++++++++++++------ src/types/signature.rs | 19 +++++++++++++++++-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 59e7148..3538436 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -1,6 +1,6 @@ use curve25519_dalek::edwards::CompressedEdwardsY; -use ed25519_dalek::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey, SecretKey, - SignatureError as Ed25519SignatureError, Signer}; +use ed25519_dalek::{ExpandedSecretKey, PublicKey as Ed25519PublicKey, SecretKey, + SignatureError as Ed25519SignatureError, Signer, Verifier}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use thiserror::Error; @@ -21,21 +21,51 @@ pub enum KeypairError { PublicKeyParseBytes(Ed25519SignatureError), } -pub struct Keypair(pub Ed25519Keypair); +pub struct Keypair { + pub public: PublicKey, + private: PrivateKey, +} + +impl Signer for Keypair { + /// Sign a message with this keypair's secret key. + fn try_sign(&self, message: &[u8]) -> Result { + let expanded: ExpandedSecretKey = (&self.private.0).into(); + Ok(Signature::new(expanded.sign(&message, &self.public.0))) + } +} + +impl Verifier for Keypair { + /// Verify a signature on a message with this keypair's public key. + fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { + self.public.0.verify(message, &signature.0) + } +} impl Keypair { pub fn from_private_bytes(bytes: &[u8]) -> Result { let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; - let public = Ed25519PublicKey::from(&secret); - Ok(Keypair(Ed25519Keypair { secret, public })) + let public = PublicKey(Ed25519PublicKey::from(&secret)); + let private = PrivateKey(secret); + Ok(Keypair{ public, private }) } - pub fn sign(&self, message: &[u8]) -> Signature { Signature::new(self.0.sign(message)) } + pub fn sign(&self, message: &[u8]) -> Signature { Signer::sign(self, message) } + + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { Verifier::verify(self, message, signature) } } +struct PrivateKey(SecretKey); + #[derive(Clone, Debug, PartialEq)] pub struct PublicKey(pub Ed25519PublicKey); +impl Verifier for PublicKey { + /// Verify a signature on a message with this keypair's public key. + fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { + self.0.verify(message, &signature.0) + } +} + impl PublicKey { pub fn from_bytes(bytes: &[u8]) -> Result { let public_key = Ed25519PublicKey::from_bytes(bytes) diff --git a/src/types/signature.rs b/src/types/signature.rs index 01382a8..93a26a1 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -1,5 +1,6 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; +use ed25519_dalek::ed25519::signature::{Error as SignatureCrateError, Signature as SignatureTrait}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; @@ -16,7 +17,22 @@ pub enum SignatureError { } #[derive(Clone, Debug, PartialEq)] -pub struct Signature(Ed25519Signature); +pub struct Signature(pub Ed25519Signature); + +// trait bound of Signer for Keypair +impl SignatureTrait for Signature { + fn from_bytes(bytes: &[u8]) -> Result { + // Delegate to the inner type's implementation + Ed25519Signature::from_bytes(bytes).map(Signature) + } +} + +// trait bound of signature_crate::Signature +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} impl Signature { pub fn new(signature: Ed25519Signature) -> Self { Signature(signature) } @@ -50,7 +66,6 @@ impl Signature { pub fn validate_r_point(&self) -> bool { let r_bytes = &self.0.to_bytes()[0..SIGNATURE_LENGTH / 2]; - println!("r_bytes len: {}", r_bytes.len()); // Create a CompressedEdwardsY point from the first 32 bytes CompressedEdwardsY::from_slice(r_bytes).decompress().is_some() } From ae9794f52b92f38a0f5dcc8274bd613af1dbabaf Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 08:21:37 -0400 Subject: [PATCH 068/233] cargo fmt --- src/encoding.rs | 2 +- src/types/keypair.rs | 16 +++++++++------- src/types/signature.rs | 6 ++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 3b3cb11..30da71d 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -149,4 +149,4 @@ mod tests { Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() ); } -} \ No newline at end of file +} diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 3538436..8fb6299 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -46,12 +46,14 @@ impl Keypair { let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; let public = PublicKey(Ed25519PublicKey::from(&secret)); let private = PrivateKey(secret); - Ok(Keypair{ public, private }) + Ok(Keypair { public, private }) } pub fn sign(&self, message: &[u8]) -> Signature { Signer::sign(self, message) } - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { Verifier::verify(self, message, signature) } + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { + Verifier::verify(self, message, signature) + } } struct PrivateKey(SecretKey); @@ -71,7 +73,7 @@ impl PublicKey { let public_key = Ed25519PublicKey::from_bytes(bytes) .map(PublicKey) .map_err(KeypairError::PublicKeyParseBytes)?; - + match public_key.validate_point() { true => Ok(public_key), false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), @@ -81,7 +83,9 @@ impl PublicKey { /// Check if public key is a valid point on the Ed25519 curve pub fn validate_point(&self) -> bool { // Create a CompressedEdwardsY point from the first 32 bytes - CompressedEdwardsY::from_slice(&self.0.to_bytes()).decompress().is_some() + CompressedEdwardsY::from_slice(&self.0.to_bytes()) + .decompress() + .is_some() } pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes() } @@ -148,7 +152,5 @@ impl fmt::Display for PublicKey { } impl fmt::LowerHex for PublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", hex::encode(self.as_bytes())) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.as_bytes())) } } diff --git a/src/types/signature.rs b/src/types/signature.rs index 93a26a1..82168a0 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -1,6 +1,6 @@ use curve25519_dalek::edwards::CompressedEdwardsY; -use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; use ed25519_dalek::ed25519::signature::{Error as SignatureCrateError, Signature as SignatureTrait}; +use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; @@ -29,9 +29,7 @@ impl SignatureTrait for Signature { // trait bound of signature_crate::Signature impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } + fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Signature { From 4721365511dbc00582e96c142dcff36d0aaaaacc Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 08:24:22 -0400 Subject: [PATCH 069/233] move Currency public export path --- src/http/endpoints.rs | 4 ++-- src/types.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/http/endpoints.rs b/src/http/endpoints.rs index 8547532..4fd9207 100644 --- a/src/http/endpoints.rs +++ b/src/http/endpoints.rs @@ -1,6 +1,6 @@ use crate::http::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::transaction::{SiacoinElement, V1Transaction, V2Transaction}; -use crate::types::{Address, BlockID, Currency, Event, Hash256}; +use crate::transaction::{SiacoinElement, V1Transaction, V2Transaction, Currency}; +use crate::types::{Address, BlockID, Event, Hash256}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/types.rs b/src/types.rs index a0687d1..bdb5949 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,6 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; // FIXME expose these types or the entire module -pub use crate::transaction::Currency; // FIXME should be imported via the transaction module use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, V2FileContractResolution, V2Transaction}; use blake2b_simd::Params; From 5e1108d7cdbf5819be3b1a15e00c8ccfd13c483b Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 09:34:56 -0400 Subject: [PATCH 070/233] remove SpendPolicyHelper type and serde_with dep --- Cargo.lock | 70 --------------------------------------- Cargo.toml | 1 - src/spend_policy.rs | 56 +++++-------------------------- src/tests/spend_policy.rs | 16 ++++----- src/transaction.rs | 5 +-- 5 files changed, 17 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b7b332..0ec2a60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,41 +256,6 @@ dependencies = [ "syn 1.0.95", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.95", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.95", -] - [[package]] name = "derive_more" version = "0.99.11" @@ -686,12 +651,6 @@ dependencies = [ "cxx-build", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.2.3" @@ -1269,28 +1228,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.95", -] - [[package]] name = "sha2" version = "0.9.9" @@ -1329,7 +1266,6 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_json", - "serde_with", "thiserror", "tokio", "url", @@ -1376,12 +1312,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "subtle" version = "2.4.0" diff --git a/Cargo.toml b/Cargo.toml index 076c6b8..d1458cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ ed25519-dalek = { version = "1.0.1", features = ["serde"] } curve25519-dalek = "3.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1", features = ["preserve_order", "raw_value"] } -serde_with = "1.14.0" nom = "6.1.2" blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } diff --git a/src/spend_policy.rs b/src/spend_policy.rs index a80de67..c2c75c4 100644 --- a/src/spend_policy.rs +++ b/src/spend_policy.rs @@ -15,64 +15,24 @@ use std::str::FromStr; const POLICY_VERSION: u8 = 1u8; #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(tag = "type", content = "policy")] pub enum SpendPolicy { + #[serde(rename = "above")] Above(u64), + #[serde(rename = "after")] After(u64), + #[serde(rename = "pk")] PublicKey(PublicKey), + #[serde(rename = "h")] Hash(Hash256), + #[serde(rename = "thresh")] Threshold { n: u8, of: Vec }, + #[serde(rename = "opaque")] Opaque(Address), + #[serde(rename = "uc")] UnlockConditions(UnlockCondition), // For v1 compatibility } -// FIXME this can now be fully removed now that Prefixed* types are removed -/// Helper to serialize/deserialize SpendPolicy with prefixed PublicKey and H256 -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -#[serde(tag = "type", content = "policy", rename_all = "camelCase")] -pub enum SpendPolicyHelper { - Above(u64), - After(u64), - Pk(PublicKey), - H(Hash256), - Thresh { n: u8, of: Vec }, - Opaque(Address), - Uc(UnlockCondition), // For v1 compatibility -} - -impl From for SpendPolicy { - fn from(helper: SpendPolicyHelper) -> Self { - match helper { - SpendPolicyHelper::Above(height) => SpendPolicy::Above(height), - SpendPolicyHelper::After(time) => SpendPolicy::After(time), - SpendPolicyHelper::Pk(pk) => SpendPolicy::PublicKey(pk), - SpendPolicyHelper::H(hash) => SpendPolicy::Hash(hash), - SpendPolicyHelper::Thresh { n, of } => SpendPolicy::Threshold { - n, - of: of.into_iter().map(SpendPolicy::from).collect(), - }, - SpendPolicyHelper::Opaque(address) => SpendPolicy::Opaque(address), - SpendPolicyHelper::Uc(uc) => SpendPolicy::UnlockConditions(uc), - } - } -} - -impl From for SpendPolicyHelper { - fn from(policy: SpendPolicy) -> Self { - match policy { - SpendPolicy::Above(height) => SpendPolicyHelper::Above(height), - SpendPolicy::After(time) => SpendPolicyHelper::After(time), - SpendPolicy::PublicKey(pk) => SpendPolicyHelper::Pk(pk), - SpendPolicy::Hash(hash) => SpendPolicyHelper::H(hash), - SpendPolicy::Threshold { n, of } => SpendPolicyHelper::Thresh { - n, - of: of.into_iter().map(SpendPolicyHelper::from).collect(), - }, - SpendPolicy::Opaque(address) => SpendPolicyHelper::Opaque(address), - SpendPolicy::UnlockConditions(uc) => SpendPolicyHelper::Uc(uc), - } - } -} - impl Encodable for SpendPolicy { fn encode(&self, encoder: &mut Encoder) { encoder.write_u8(POLICY_VERSION); diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 6f5b39a..18e726e 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,4 +1,4 @@ -use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; +use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; use crate::types::{Address, Hash256, PublicKey}; use std::convert::TryFrom; use std::str::FromStr; @@ -12,7 +12,7 @@ fn test_serde_spend_policy_above() { } ); - let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Above(100); assert_eq!(spend_policy, spend_policy_deser); @@ -27,7 +27,7 @@ fn test_serde_spend_policy_after() { } ); - let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::After(200); assert_eq!(spend_policy, spend_policy_deser); @@ -45,7 +45,7 @@ fn test_serde_spend_policy_public_key() { &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::PublicKey(pubkey); assert_eq!(spend_policy, spend_policy_deser); @@ -60,7 +60,7 @@ fn test_serde_spend_policy_hash() { } ); let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Hash(hash); assert_eq!(spend_policy, spend_policy_deser); @@ -76,7 +76,7 @@ fn test_serde_spend_policy_opaque() { ); let address = Address::from_str("addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a").unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Opaque(address); assert_eq!(spend_policy, spend_policy_deser); @@ -127,7 +127,7 @@ fn test_serde_spend_policy_threshold() { } ); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); assert_eq!(spend_policy, spend_policy_deser); } @@ -158,7 +158,7 @@ fn test_serde_spend_policy_unlock_conditions_standard() { signatures_required: 1, }; - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::UnlockConditions(uc); assert_eq!(spend_policy, spend_policy_deser); diff --git a/src/transaction.rs b/src/transaction.rs index bbba00a..b34181e 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,10 +1,9 @@ use crate::encoding::{Encodable, Encoder}; -use crate::spend_policy::{SpendPolicy, SpendPolicyHelper, UnlockCondition, UnlockKey}; +use crate::spend_policy::{SpendPolicy, UnlockCondition, UnlockKey}; use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; -use serde_with::{serde_as, FromInto}; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::ops::Deref; @@ -107,11 +106,9 @@ impl<'a> Encodable for CurrencyVersion<'a> { pub type Preimage = Vec; -#[serde_as] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(deny_unknown_fields)] pub struct SatisfiedPolicy { - #[serde_as(as = "FromInto")] pub policy: SpendPolicy, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub signatures: Vec, From 19321cc6122df30fbef185a023afc8eabb888495 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 09:35:25 -0400 Subject: [PATCH 071/233] cargo fmt --- src/http/endpoints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/endpoints.rs b/src/http/endpoints.rs index 4fd9207..6864267 100644 --- a/src/http/endpoints.rs +++ b/src/http/endpoints.rs @@ -1,5 +1,5 @@ use crate::http::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::transaction::{SiacoinElement, V1Transaction, V2Transaction, Currency}; +use crate::transaction::{Currency, SiacoinElement, V1Transaction, V2Transaction}; use crate::types::{Address, BlockID, Event, Hash256}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; From 64ccd8a5a91236886bc5948b71240d9f75f7a34a Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 09:57:34 -0400 Subject: [PATCH 072/233] majorly reorganize pub exports --- src/blake2b_internal.rs | 4 +--- src/http/endpoints.rs | 3 +-- src/lib.rs | 3 --- src/tests/encoding.rs | 3 +-- src/tests/serde.rs | 4 +--- src/tests/spend_policy.rs | 3 +-- src/tests/transaction.rs | 10 ++++------ src/types.rs | 12 +++++++++--- src/{ => types}/specifier.rs | 0 src/{ => types}/spend_policy.rs | 4 +--- src/{ => types}/transaction.rs | 3 +-- 11 files changed, 20 insertions(+), 29 deletions(-) rename src/{ => types}/specifier.rs (100%) rename src/{ => types}/spend_policy.rs (98%) rename src/{ => types}/transaction.rs (99%) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index af91740..cfeec96 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -1,6 +1,4 @@ -use crate::specifier::Specifier; -use crate::spend_policy::UnlockKey; -use crate::types::{Hash256, PublicKey}; +use crate::types::{Hash256, PublicKey, Specifier, UnlockKey}; use blake2b_simd::Params; use std::default::Default; diff --git a/src/http/endpoints.rs b/src/http/endpoints.rs index 6864267..f2d4a79 100644 --- a/src/http/endpoints.rs +++ b/src/http/endpoints.rs @@ -1,6 +1,5 @@ use crate::http::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::transaction::{Currency, SiacoinElement, V1Transaction, V2Transaction}; -use crate::types::{Address, BlockID, Event, Hash256}; +use crate::types::{Address, BlockID, Event, Hash256, Currency, SiacoinElement, V1Transaction, V2Transaction}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; diff --git a/src/lib.rs b/src/lib.rs index 712c7cc..fecce53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,6 @@ mod macros; pub mod blake2b_internal; pub mod encoding; pub mod http; -pub mod specifier; -pub mod spend_policy; -pub mod transaction; pub mod types; #[cfg(test)] mod tests; diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 0698d79..c2d90a5 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -1,7 +1,6 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::Encoder; -use crate::spend_policy::{SpendPolicy, UnlockCondition}; -use crate::types::{Address, Hash256, PublicKey}; +use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition}; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tests/serde.rs b/src/tests/serde.rs index a2f40b2..34b0d15 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,6 +1,4 @@ -use crate::spend_policy::UnlockKey; -use crate::transaction::{SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; -use crate::types::{Address, BlockID, Event, Hash256}; +use crate::types::{Address, BlockID, Event, Hash256, UnlockKey, SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; // Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) macro_rules! test_serde { diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 18e726e..14d3148 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,5 +1,4 @@ -use crate::spend_policy::{spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; -use crate::types::{Address, Hash256, PublicKey}; +use crate::types::{Address, Hash256, PublicKey, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 7f85ff9..5c2b553 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -1,10 +1,8 @@ use crate::encoding::Encoder; -use crate::spend_policy::{spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, - UnlockCondition}; -use crate::transaction::{Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, - SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, - StateElement, V2FileContract, V2FileContractElement, V2Transaction}; -use crate::types::{v1_standard_address_from_pubkey, Address, Hash256, Keypair, PublicKey, Signature}; +use crate::types::{Address, Hash256, Keypair, PublicKey, Signature, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, + SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, + StateElement, V2FileContract, V2FileContractElement, V2Transaction, spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, + UnlockCondition}; use std::convert::TryFrom; use std::str::FromStr; diff --git a/src/types.rs b/src/types.rs index bdb5949..b8ebdbe 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,8 +1,5 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; -// FIXME expose these types or the entire module -use crate::transaction::{FileContractElementV1, SiacoinElement, SiafundElement, StateElement, V1Transaction, - V2FileContractResolution, V2Transaction}; use blake2b_simd::Params; use chrono::{DateTime, Utc}; use hex::FromHexError; @@ -22,6 +19,15 @@ pub use signature::{Signature, SignatureError}; mod keypair; pub use keypair::{Keypair, KeypairError, PublicKey}; +mod spend_policy; +pub use spend_policy::*; + +mod transaction; +pub use transaction::*; + +mod specifier; +pub use specifier::*; + const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; diff --git a/src/specifier.rs b/src/types/specifier.rs similarity index 100% rename from src/specifier.rs rename to src/types/specifier.rs diff --git a/src/spend_policy.rs b/src/types/spend_policy.rs similarity index 98% rename from src/spend_policy.rs rename to src/types/spend_policy.rs index c2c75c4..f8322c9 100644 --- a/src/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -1,8 +1,6 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, Accumulator}; use crate::encoding::{Encodable, Encoder}; -use crate::specifier::Specifier; -use crate::transaction::{Preimage, SatisfiedPolicy}; -use crate::types::{Address, Hash256, PublicKey, Signature}; +use crate::types::{Address, Hash256, PublicKey, Signature, Specifier, Preimage, SatisfiedPolicy}; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; use nom::combinator::all_consuming; diff --git a/src/transaction.rs b/src/types/transaction.rs similarity index 99% rename from src/transaction.rs rename to src/types/transaction.rs index b34181e..838d954 100644 --- a/src/transaction.rs +++ b/src/types/transaction.rs @@ -1,6 +1,5 @@ use crate::encoding::{Encodable, Encoder}; -use crate::spend_policy::{SpendPolicy, UnlockCondition, UnlockKey}; -use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature}; +use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature, SpendPolicy, UnlockCondition, UnlockKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; From bad7478e61a7aff1093ff64c15560d5f83eb11b1 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 09:58:19 -0400 Subject: [PATCH 073/233] remove standalone v1_standard_address_from_pubkey; add Address method instead --- src/tests/transaction.rs | 12 ++++++------ src/types.rs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 5c2b553..0d630ce 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -445,8 +445,8 @@ fn test_file_contract_v2_encode() { let sig1 = Signature::from_bytes( &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - let address0 = v1_standard_address_from_pubkey(&pubkey0); - let address1 = v1_standard_address_from_pubkey(&pubkey1); + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { value: 1.into(), @@ -494,8 +494,8 @@ fn test_file_contract_element_v2_encode() { let sig1 = Signature::from_bytes( &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - let address0 = v1_standard_address_from_pubkey(&pubkey0); - let address1 = v1_standard_address_from_pubkey(&pubkey1); + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { value: 1.into(), @@ -557,8 +557,8 @@ fn test_file_contract_revision_v2_encode() { let sig1 = Signature::from_bytes( &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - let address0 = v1_standard_address_from_pubkey(&pubkey0); - let address1 = v1_standard_address_from_pubkey(&pubkey1); + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { value: 1.into(), diff --git a/src/types.rs b/src/types.rs index b8ebdbe..cea420f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -78,6 +78,11 @@ impl Address { let checksum = blake2b_checksum(bytes); format!("{}{}", hex::encode(bytes), hex::encode(checksum)) } + + pub fn standard_address_v1(pubkey: &PublicKey) -> Self { + let hash = standard_unlock_hash(pubkey); + Address(hash) + } } impl Encodable for Address { @@ -144,11 +149,6 @@ fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { hash.as_bytes()[0..6].try_into().expect("array is 64 bytes long") } -pub fn v1_standard_address_from_pubkey(pubkey: &PublicKey) -> Address { - let hash = standard_unlock_hash(pubkey); - Address(hash) -} - #[derive(Clone, Debug, PartialEq)] pub struct BlockID(pub Hash256); From 9594d131178881d7e11d290f5f409d6a849ef1e9 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:02:53 -0400 Subject: [PATCH 074/233] remove now irrelevant comment --- src/types/transaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 838d954..5372fde 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -329,7 +329,6 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { #[derive(Clone, Debug, PartialEq)] pub struct SiacoinOutputId(pub Hash256); -// FIXME this code pattern is reoccuring in many places and should be generalized with helpers or macros impl<'de> Deserialize<'de> for SiacoinOutputId { fn deserialize(deserializer: D) -> Result where From f399db950865b24ec31f591eefd4c68d032c4417 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:03:20 -0400 Subject: [PATCH 075/233] fix wasm test warnings --- src/http/client/wasm.rs | 7 ++----- src/http/client/wasm/wasm_fetch.rs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/http/client/wasm.rs b/src/http/client/wasm.rs index ddd4422..fec5616 100644 --- a/src/http/client/wasm.rs +++ b/src/http/client/wasm.rs @@ -118,10 +118,7 @@ impl ApiClientHelpers for Client {} #[cfg(all(target_arch = "wasm32", test))] mod wasm_tests { use super::*; - use blake2b_simd::Hash; - use log::info; use once_cell::sync::Lazy; - use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -134,7 +131,7 @@ mod wasm_tests { #[wasm_bindgen_test] async fn test_sia_wasm_client_client_error() { use crate::http::endpoints::TxpoolBroadcastRequest; - use crate::transaction::V2Transaction; + use crate::types::V2Transaction; let client = Client::new(CONF.clone()).await.unwrap(); let tx_str = r#" @@ -191,7 +188,7 @@ mod wasm_tests { match client.dispatcher(req).await.expect_err("Expected HTTP 400 error") { ApiClientError::UnexpectedHttpStatus { status: StatusCode::BAD_REQUEST, - body, + body: _, } => (), e => panic!("Unexpected error: {:?}", e), } diff --git a/src/http/client/wasm/wasm_fetch.rs b/src/http/client/wasm/wasm_fetch.rs index 5040d96..54a5460 100644 --- a/src/http/client/wasm/wasm_fetch.rs +++ b/src/http/client/wasm/wasm_fetch.rs @@ -276,7 +276,7 @@ mod tests { let uri = "http://example.com"; let mut req_init = RequestInit::new(); req_init.method("GET"); - let js_request = JsRequest::new_with_str_and_init(&uri, &req_init).unwrap(); + let _js_request = JsRequest::new_with_str_and_init(&uri, &req_init).unwrap(); } // further unit tests could be implemented for the Err case based on the spec From e32d8b00f155eb501a68af7b2256449eab58a055 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:40:17 -0400 Subject: [PATCH 076/233] wrap blake2b unit tests in cross_target_tests --- src/blake2b_internal.rs | 341 ++++++++++++++++++++-------------------- 1 file changed, 168 insertions(+), 173 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index cfeec96..d878b89 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -151,178 +151,173 @@ fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> Hash256 { Hash256(array) } -#[test] -fn test_accumulator_new() { - let default_accumulator = Accumulator::default(); - - let expected = Accumulator { - trees: std::array::from_fn(|_| Hash256::default()), - num_leaves: 0, - }; - assert_eq!(default_accumulator, expected) -} - -#[test] -fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), Hash256::default()) } - -#[test] -fn test_accumulator_root() { - let mut accumulator = Accumulator::default(); - - let timelock_leaf = timelock_leaf(0u64); - accumulator.add_leaf(timelock_leaf); - - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - accumulator.add_leaf(pubkey_leaf); - - let sigs_required_leaf = sigs_required_leaf(1u64); - accumulator.add_leaf(sigs_required_leaf); - - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); - assert_eq!(accumulator.root(), expected); -} - -#[test] -fn test_accumulator_add_leaf_standard_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(1u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); - assert_eq!(root, expected) -} - -#[test] -fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey1 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); - let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); - - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(2u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey1_leaf); - accumulator.add_leaf(pubkey2_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); - assert_eq!(root, expected) -} - -#[test] -fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { - let mut accumulator = Accumulator::default(); - - let pubkey1 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); - let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); - - let timelock_leaf = timelock_leaf(0u64); - let sigs_required_leaf = sigs_required_leaf(1u64); - - accumulator.add_leaf(timelock_leaf); - accumulator.add_leaf(pubkey1_leaf); - accumulator.add_leaf(pubkey2_leaf); - accumulator.add_leaf(sigs_required_leaf); - - let root = accumulator.root(); - let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); - assert_eq!(root, expected) -} - -#[test] -fn test_standard_unlock_hash() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let hash = standard_unlock_hash(&pubkey); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); - assert_eq!(hash, expected) -} - -#[test] -fn test_hash_blake2b_pair() { - let left: [u8; 32] = hex::decode("cdcce3978a58ceb6c8480d218646db4eae85eb9ea9c2f5138fbacb4ce2c701e3") - .unwrap() - .try_into() - .unwrap(); - let right: [u8; 32] = hex::decode("b36010eb285c154a8cd63084acbe7eac0c4d625ab4e1a76e624a8798cb63497b") - .unwrap() - .try_into() - .unwrap(); - - let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); - assert_eq!(hash, expected) -} - -#[test] -fn test_timelock_leaf() { - let hash = timelock_leaf(0); - let expected = Hash256(STANDARD_TIMELOCK_BLAKE2B_HASH); - assert_eq!(hash, expected) -} - -#[test] -fn test_sigs_required_leaf() { - let hash = sigs_required_leaf(1u64); - let expected = Hash256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); - assert_eq!(hash, expected) -} - -#[test] -fn test_hash_blake2b_single() { - let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); - assert_eq!(hash, expected) -} - -#[test] -fn test_public_key_leaf() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); +#[cfg(test)] +mod test { + use super::*; + + cross_target_tests!{ + fn test_accumulator_new() { + let default_accumulator = Accumulator::default(); + + let expected = Accumulator { + trees: std::array::from_fn(|_| Hash256::default()), + num_leaves: 0, + }; + assert_eq!(default_accumulator, expected) + } - let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); - assert_eq!(hash, expected) + fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), Hash256::default()) } + + fn test_accumulator_root() { + let mut accumulator = Accumulator::default(); + + let timelock_leaf = timelock_leaf(0u64); + accumulator.add_leaf(timelock_leaf); + + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); + accumulator.add_leaf(pubkey_leaf); + + let sigs_required_leaf = sigs_required_leaf(1u64); + accumulator.add_leaf(sigs_required_leaf); + + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + assert_eq!(accumulator.root(), expected); + } + + fn test_accumulator_add_leaf_standard_unlock_hash() { + let mut accumulator = Accumulator::default(); + + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); + let timelock_leaf = timelock_leaf(0u64); + let sigs_required_leaf = sigs_required_leaf(1u64); + + accumulator.add_leaf(timelock_leaf); + accumulator.add_leaf(pubkey_leaf); + accumulator.add_leaf(sigs_required_leaf); + + let root = accumulator.root(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + assert_eq!(root, expected) + } + + fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { + let mut accumulator = Accumulator::default(); + + let pubkey1 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey2 = PublicKey::from_bytes( + &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); + let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); + + let timelock_leaf = timelock_leaf(0u64); + let sigs_required_leaf = sigs_required_leaf(2u64); + + accumulator.add_leaf(timelock_leaf); + accumulator.add_leaf(pubkey1_leaf); + accumulator.add_leaf(pubkey2_leaf); + accumulator.add_leaf(sigs_required_leaf); + + let root = accumulator.root(); + let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + assert_eq!(root, expected) + } + + fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { + let mut accumulator = Accumulator::default(); + + let pubkey1 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey2 = PublicKey::from_bytes( + &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); + let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); + + let timelock_leaf = timelock_leaf(0u64); + let sigs_required_leaf = sigs_required_leaf(1u64); + + accumulator.add_leaf(timelock_leaf); + accumulator.add_leaf(pubkey1_leaf); + accumulator.add_leaf(pubkey2_leaf); + accumulator.add_leaf(sigs_required_leaf); + + let root = accumulator.root(); + let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + assert_eq!(root, expected) + } + + fn test_standard_unlock_hash() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let hash = standard_unlock_hash(&pubkey); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + assert_eq!(hash, expected) + } + + fn test_hash_blake2b_pair() { + let left: [u8; 32] = hex::decode("cdcce3978a58ceb6c8480d218646db4eae85eb9ea9c2f5138fbacb4ce2c701e3") + .unwrap() + .try_into() + .unwrap(); + let right: [u8; 32] = hex::decode("b36010eb285c154a8cd63084acbe7eac0c4d625ab4e1a76e624a8798cb63497b") + .unwrap() + .try_into() + .unwrap(); + + let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + assert_eq!(hash, expected) + } + + fn test_timelock_leaf() { + let hash = timelock_leaf(0); + let expected = Hash256(STANDARD_TIMELOCK_BLAKE2B_HASH); + assert_eq!(hash, expected) + } + + fn test_sigs_required_leaf() { + let hash = sigs_required_leaf(1u64); + let expected = Hash256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); + assert_eq!(hash, expected) + } + + fn test_hash_blake2b_single() { + let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); + let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + assert_eq!(hash, expected) + } + + fn test_public_key_leaf() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); + let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + assert_eq!(hash, expected) + } + } } From 11299fb8ba7214e8b8afc41e49be60341bcea37d Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:43:01 -0400 Subject: [PATCH 077/233] wrap encoder tests in cross_target_tests --- src/encoding.rs | 156 +++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 81 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index 30da71d..3d97ca0 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -58,95 +58,89 @@ impl Encodable for Hash256 { } #[cfg(test)] -mod tests { +mod test { use super::*; use std::convert::TryFrom; - #[test] - fn test_encoder_default_hash() { - assert_eq!( - Encoder::default().hash(), - Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() - ) - } + cross_target_tests!{ + fn test_encoder_default_hash() { + assert_eq!( + Encoder::default().hash(), + Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + ) + } - #[test] - fn test_encoder_write_bytes() { - let mut encoder = Encoder::default(); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() - ); - } + fn test_encoder_write_bytes() { + let mut encoder = Encoder::default(); + encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() + ); + } - #[test] - fn test_encoder_write_u8() { - let mut encoder = Encoder::default(); - encoder.write_u8(1); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); - } + fn test_encoder_write_u8() { + let mut encoder = Encoder::default(); + encoder.write_u8(1); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + } - #[test] - fn test_encoder_write_u64() { - let mut encoder = Encoder::default(); - encoder.write_u64(1); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() - ); - } + fn test_encoder_write_u64() { + let mut encoder = Encoder::default(); + encoder.write_u64(1); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() + ); + } - #[test] - fn test_encoder_write_distiguisher() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() - ); - } + fn test_encoder_write_distiguisher() { + let mut encoder = Encoder::default(); + encoder.write_distinguisher("test"); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() + ); + } - #[test] - fn test_encoder_write_bool() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); - } + fn test_encoder_write_bool() { + let mut encoder = Encoder::default(); + encoder.write_bool(true); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + } - #[test] - fn test_encoder_reset() { - let mut encoder = Encoder::default(); - encoder.write_bool(true); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() - ); - - encoder.reset(); - encoder.write_bool(false); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() - ); - } + fn test_encoder_reset() { + let mut encoder = Encoder::default(); + encoder.write_bool(true); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + ); + + encoder.reset(); + encoder.write_bool(false); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() + ); + } - #[test] - fn test_encoder_complex() { - let mut encoder = Encoder::default(); - encoder.write_distinguisher("test"); - encoder.write_bool(true); - encoder.write_u8(1); - encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); - assert_eq!( - encoder.hash(), - Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() - ); + fn test_encoder_complex() { + let mut encoder = Encoder::default(); + encoder.write_distinguisher("test"); + encoder.write_bool(true); + encoder.write_u8(1); + encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); + assert_eq!( + encoder.hash(), + Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() + ); + } } -} +} \ No newline at end of file From a73027d4642346f7c152ac4da3f69a1c6fd7bc21 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:45:40 -0400 Subject: [PATCH 078/233] wrap tests/encoding in cross_target_tests --- src/tests/encoding.rs | 355 +++++++++++++++++++++--------------------- 1 file changed, 175 insertions(+), 180 deletions(-) diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index c2d90a5..126e50f 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -1,180 +1,175 @@ -use crate::blake2b_internal::standard_unlock_hash; -use crate::encoding::Encoder; -use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition}; -use std::convert::TryFrom; -use std::str::FromStr; - -#[test] -fn test_unlock_condition_unlock_hash_2of2_multisig() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); - - let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_unlock_condition_unlock_hash_1of2_multisig() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); - - let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_spend_policy_encode_above() { - let policy = SpendPolicy::above(1); - - let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:188b997bb99dee13e95f92c3ea150bd76b3ec72e5ba57b0d57439a1a6e2865e9b25ea5d1825e").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_after() { - let policy = SpendPolicy::after(1); - let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:60c74e0ce5cede0f13f83b0132cb195c995bc7688c9fac34bbf2b14e14394b8bbe2991bc017f").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_pubkey() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let policy = SpendPolicy::PublicKey(pubkey); - - let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:55a7793237722c6df8222fd512063cb74228085ef1805c5184713648c159b919ac792fbad0e1").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_hash() { - let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); - let policy = SpendPolicy::Hash(hash); - - let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:a4d5a06d8d3c2e45aa26627858ce8e881505ae3c9d122a1d282c7824163751936cffb347e435").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_threshold() { - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![SpendPolicy::above(1), SpendPolicy::after(1)], - }; - - let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); - assert_eq!(hash, expected); - - let address = policy.address(); - let expected = - Address::from_str("addr:4179b53aba165e46e4c85b3c8766bb758fb6f0bfa5721550b81981a3ec38efc460557dc1ded4").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_spend_policy_encode_unlock_condition() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); - - let sub_policy = SpendPolicy::UnlockConditions(unlock_condition); - let base_address = sub_policy.address(); - let expected = - Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a").unwrap(); - assert_eq!(base_address, expected); - - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![sub_policy], - }; - let address = policy.address(); - let expected = - Address::from_str("addr:1498a58c843ce66740e52421632d67a0f6991ea96db1fc97c29e46f89ae56e3534078876331d").unwrap(); - assert_eq!(address, expected); -} - -#[test] -fn test_unlock_condition_encode() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); - - let hash = Encoder::encode_and_hash(&unlock_condition); - let expected = Hash256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_public_key_encode() { - let public_key = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let hash = Encoder::encode_and_hash(&public_key); - let expected = Hash256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_unlock_condition_unlock_hash_standard() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![pubkey.clone()], 0, 1); - - let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); - assert_eq!(hash, expected); - let hash = standard_unlock_hash(&pubkey); - assert_eq!(hash, expected); -} + +#[cfg(test)] +mod test { + use crate::blake2b_internal::standard_unlock_hash; + use crate::encoding::Encoder; + use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition}; + use std::convert::TryFrom; + use std::str::FromStr; + + cross_target_tests!{ + fn test_unlock_condition_unlock_hash_2of2_multisig() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey2 = PublicKey::from_bytes( + &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); + + let hash = unlock_condition.unlock_hash(); + let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + assert_eq!(hash, expected); + } + + fn test_unlock_condition_unlock_hash_1of2_multisig() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey2 = PublicKey::from_bytes( + &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); + + let hash = unlock_condition.unlock_hash(); + let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + assert_eq!(hash, expected); + } + + fn test_spend_policy_encode_above() { + let policy = SpendPolicy::above(1); + + let hash = Encoder::encode_and_hash(&policy); + let expected = Hash256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); + assert_eq!(hash, expected); + + let address = policy.address(); + let expected = + Address::from_str("addr:188b997bb99dee13e95f92c3ea150bd76b3ec72e5ba57b0d57439a1a6e2865e9b25ea5d1825e").unwrap(); + assert_eq!(address, expected); + } + + fn test_spend_policy_encode_after() { + let policy = SpendPolicy::after(1); + let hash = Encoder::encode_and_hash(&policy); + let expected = Hash256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); + assert_eq!(hash, expected); + + let address = policy.address(); + let expected = + Address::from_str("addr:60c74e0ce5cede0f13f83b0132cb195c995bc7688c9fac34bbf2b14e14394b8bbe2991bc017f").unwrap(); + assert_eq!(address, expected); + } + + fn test_spend_policy_encode_pubkey() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let policy = SpendPolicy::PublicKey(pubkey); + + let hash = Encoder::encode_and_hash(&policy); + let expected = Hash256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); + assert_eq!(hash, expected); + + let address = policy.address(); + let expected = + Address::from_str("addr:55a7793237722c6df8222fd512063cb74228085ef1805c5184713648c159b919ac792fbad0e1").unwrap(); + assert_eq!(address, expected); + } + + fn test_spend_policy_encode_hash() { + let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let policy = SpendPolicy::Hash(hash); + + let hash = Encoder::encode_and_hash(&policy); + let expected = Hash256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); + assert_eq!(hash, expected); + + let address = policy.address(); + let expected = + Address::from_str("addr:a4d5a06d8d3c2e45aa26627858ce8e881505ae3c9d122a1d282c7824163751936cffb347e435").unwrap(); + assert_eq!(address, expected); + } + + fn test_spend_policy_encode_threshold() { + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![SpendPolicy::above(1), SpendPolicy::after(1)], + }; + + let hash = Encoder::encode_and_hash(&policy); + let expected = Hash256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); + assert_eq!(hash, expected); + + let address = policy.address(); + let expected = + Address::from_str("addr:4179b53aba165e46e4c85b3c8766bb758fb6f0bfa5721550b81981a3ec38efc460557dc1ded4").unwrap(); + assert_eq!(address, expected); + } + + fn test_spend_policy_encode_unlock_condition() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); + + let sub_policy = SpendPolicy::UnlockConditions(unlock_condition); + let base_address = sub_policy.address(); + let expected = + Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a").unwrap(); + assert_eq!(base_address, expected); + + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![sub_policy], + }; + let address = policy.address(); + let expected = + Address::from_str("addr:1498a58c843ce66740e52421632d67a0f6991ea96db1fc97c29e46f89ae56e3534078876331d").unwrap(); + assert_eq!(address, expected); + } + + fn test_unlock_condition_encode() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); + + let hash = Encoder::encode_and_hash(&unlock_condition); + let expected = Hash256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); + assert_eq!(hash, expected); + } + + fn test_public_key_encode() { + let public_key = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let hash = Encoder::encode_and_hash(&public_key); + let expected = Hash256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); + assert_eq!(hash, expected); + } + + fn test_unlock_condition_unlock_hash_standard() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![pubkey.clone()], 0, 1); + + let hash = unlock_condition.unlock_hash(); + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + assert_eq!(hash, expected); + let hash = standard_unlock_hash(&pubkey); + assert_eq!(hash, expected); + } + } +} \ No newline at end of file From ab1df509968c098a2dbc412e47c608ba48618a3d Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 11:57:54 -0400 Subject: [PATCH 079/233] wrap tests/serde in cross_target_tests; fix indentation --- src/tests/serde.rs | 879 ++++++++++++++++++++++----------------------- 1 file changed, 437 insertions(+), 442 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 34b0d15..899f3cc 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,374 +1,23 @@ -use crate::types::{Address, BlockID, Event, Hash256, UnlockKey, SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; -// Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) -macro_rules! test_serde { - ($type:ty, $json_value:expr) => {{ - let json_str = $json_value.to_string(); - let value: $type = serde_json::from_str(&json_str).unwrap(); - let serialized = serde_json::to_string(&value).unwrap(); - let serialized_json_value: serde_json::Value = serde_json::from_str(&serialized).unwrap(); - assert_eq!($json_value, serialized_json_value); - }}; -} - -// FIXME reminder to populate the following tests -#[test] -#[ignore] -fn test_serde_block_id() { - test_serde!( - BlockID, - json!("bid:c67c3b2e57490617a25a9fcb9fd54ab6acbe72fc1e4f1f432cb9334177917667") - ); - test_serde!(BlockID, json!("bid:badc0de")); - test_serde!(BlockID, json!("bid:1badc0de")); - test_serde!(BlockID, json!("1badc0de")); - test_serde!(BlockID, json!(1)); -} - -#[test] -fn test_serde_address() { - test_serde!( - Address, - json!("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") - ); -} - -#[test] -fn test_serde_unlock_key() { - test_serde!( - UnlockKey, - json!("ed25519:0102030000000000000000000000000000000000000000000000000000000000") - ); -} - -#[test] -fn test_serde_sia_hash() { - test_serde!( - Hash256, - json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") - ); -} - -#[test] -fn test_serde_siacoin_output() { - let j = json!({ - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - }); - test_serde!(SiacoinOutput, j); -} - -#[test] -fn test_serde_state_element() { - let j = json!({ - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": null - }); - serde_json::from_value::(j).unwrap(); -} - -#[test] -fn test_serde_siacoin_element() { - let j = json!( { - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": ["h:8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], - "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - }, - "maturityHeight": 154 - } - ); - serde_json::from_value::(j).unwrap(); -} - -#[test] -fn test_serde_siacoin_element_null_merkle_proof() { - let j = json!( { - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": null, - "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - }, - "maturityHeight": 154 - } - ); - serde_json::from_value::(j).unwrap(); -} - -#[test] -fn test_serde_event_v2_contract_resolution_storage_proof() { - let j = json!( - { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", - "index": { - "height": 201, - "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 345, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", - "leafIndex": 397, - "merkleProof": [ - "h:4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", - "h:e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", - "h:0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", - "h:37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", - "h:077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 200, - "expirationHeight": 210, - "renterOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "hostOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d", - "hostSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d" - } - }, - "type": "storageProof", - "resolution": { - "proofIndex": { - "id": "h:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", - "leafIndex": 416, - "merkleProof": [], - "chainIndex": { - "height": 200, - "id": "bid:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290" - } - }, - "leaf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "proof": [] - } - }, - "siacoinElement": { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", - "leafIndex": 418, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 345 - }, - "missed": false - } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); - - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde -} - -#[test] -fn test_serde_event_v2_contract_resolution_renewal() { - let j = json!( - { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", - "index": { - "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 347, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", - "leafIndex": 423, - "merkleProof": [ - "h:ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", - "h:147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", - "h:1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03", - "hostSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03" - } - }, - "type": "renewal", - "resolution": { - "finalRevision": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 18446744073709551615u64, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "newContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 221, - "expirationHeight": 231, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "renterRollover": "0", - "hostRollover": "0", - "renterSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08", - "hostSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08" - } - }, - "siacoinElement": { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", - "leafIndex": 427, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 347 - }, - "missed": false - } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); - - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde -} - -#[test] -#[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) -fn test_serde_event_v2_contract_resolution_expiration() { - let j = json!( - { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", - "index": { - "height": 190, - "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 334, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", - "leafIndex": 351, - "merkleProof": [ - "h:e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", - "h:70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", - "h:42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", - "h:4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", - "h:59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", - "h:f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", - "h:3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 179, - "expirationHeight": 189, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", - "hostSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" - } - }, - "type": "expiration", - "resolution": {} - }, - "siacoinElement": { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", - "leafIndex": 391, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 334 - }, - "missed": true - } - } - ); - let _event = serde_json::from_value::(j).unwrap(); -} +// // FIXME reminder to populate the following tests +// #[test] +// #[ignore] +// fn test_serde_block_id() { +// test_serde!( +// BlockID, +// json!("bid:c67c3b2e57490617a25a9fcb9fd54ab6acbe72fc1e4f1f432cb9334177917667") +// ); +// test_serde!(BlockID, json!("bid:badc0de")); +// test_serde!(BlockID, json!("bid:1badc0de")); +// test_serde!(BlockID, json!("1badc0de")); +// test_serde!(BlockID, json!(1)); +// } #[test] -#[ignore] // I don't have a good test case for this yet because wallet_test.go TestEventTypes doesn't output this type +#[ignore] // FIXME I don't have a good test case for this yet because wallet_test.go TestEventTypes doesn't output this type fn test_serde_event_v2_contract_resolution_finalization() { + use crate::types::Event; let j = json!( { "id": "h:4057e021e1d6dec8d4e4ef9d6e9fa2e4491c559144848b9af5765e03b39bb69d", @@ -443,93 +92,379 @@ fn test_serde_event_v2_contract_resolution_finalization() { // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde } -#[test] -fn test_serde_event_v2_transaction() { - let j = json!( - { - "id": "h:5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", - "index": { - "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 203, - "type": "v2Transaction", - "data": { - "siacoinInputs": [ - { - "parent": { - "id": "h:78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", - "leafIndex": 421, - "merkleProof": [ - "h:f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", - "h:d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" - ], +#[cfg(test)] +mod test { + macro_rules! test_serde { + ($type:ty, $json_value:expr) => {{ + let json_str = $json_value.to_string(); + let value: $type = serde_json::from_str(&json_str).unwrap(); + let serialized = serde_json::to_string(&value).unwrap(); + let serialized_json_value: serde_json::Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!($json_value, serialized_json_value); + }}; + } + // Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) + use crate::types::{Address, Event, Hash256, UnlockKey, SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; + + cross_target_tests! { + fn test_serde_address() { + test_serde!( + Address, + json!("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") + ); + } + + fn test_serde_unlock_key() { + test_serde!( + UnlockKey, + json!("ed25519:0102030000000000000000000000000000000000000000000000000000000000") + ); + } + + fn test_serde_sia_hash() { + test_serde!( + Hash256, + json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") + ); + } + + fn test_serde_siacoin_output() { + let j = json!({ + "value": "300000000000000000000000000000", + "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + }); + test_serde!(SiacoinOutput, j); + } + + fn test_serde_state_element() { + let j = json!({ + "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": null + }); + serde_json::from_value::(j).unwrap(); + } + + fn test_serde_siacoin_element() { + let j = json!( { + "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": ["h:8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], "siacoinOutput": { - "value": "256394172736732570239334030000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "300000000000000000000000000000", + "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" }, - "maturityHeight": 0 - }, - "satisfiedPolicy": { - "policy": { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" - ], - "signaturesRequired": 1 - } + "maturityHeight": 154 + } + ); + serde_json::from_value::(j).unwrap(); + } + + fn test_serde_siacoin_element_null_merkle_proof() { + let j = json!( { + "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": null, + "siacoinOutput": { + "value": "300000000000000000000000000000", + "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + }, + "maturityHeight": 154 + } + ); + serde_json::from_value::(j).unwrap(); +} + +fn test_serde_event_v2_contract_resolution_storage_proof() { + let j = json!( + { + "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "index": { + "height": 201, + "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 345, + "type": "v2ContractResolution", + "data": { + "resolution": { + "parent": { + "id": "h:b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", + "leafIndex": 397, + "merkleProof": [ + "h:4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", + "h:e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", + "h:0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", + "h:37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", + "h:077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 200, + "expirationHeight": 210, + "renterOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "hostOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d", + "hostSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d" + } + }, + "type": "storageProof", + "resolution": { + "proofIndex": { + "id": "h:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", + "leafIndex": 416, + "merkleProof": [], + "chainIndex": { + "height": 200, + "id": "bid:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290" + } + }, + "leaf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "proof": [] + } + }, + "siacoinElement": { + "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "leafIndex": 418, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 345 }, - "signatures": [ - "sig:c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" - ] - } + "missed": false } - ], - "siacoinOutputs": [ - { - "value": "10400000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + } + ); + + let _event = serde_json::from_value::(j).unwrap(); + + // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde +} + +fn test_serde_event_v2_contract_resolution_renewal() { + let j = json!( + { + "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "index": { + "height": 203, + "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" }, - { - "value": "245994172736732570239334030000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 347, + "type": "v2ContractResolution", + "data": { + "resolution": { + "parent": { + "id": "h:06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", + "leafIndex": 423, + "merkleProof": [ + "h:ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", + "h:147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", + "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", + "h:1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03", + "hostSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03" + } + }, + "type": "renewal", + "resolution": { + "finalRevision": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 18446744073709551615u64, + "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "newContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 221, + "expirationHeight": 231, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "renterRollover": "0", + "hostRollover": "0", + "renterSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08", + "hostSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08" + } + }, + "siacoinElement": { + "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "leafIndex": 427, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 347 + }, + "missed": false } - ], - "minerFee": "0" } - } ); - test_serde!(Event, j); + + let _event = serde_json::from_value::(j).unwrap(); + + // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde } -#[test] -fn test_v2_transaction_serde_basic_send() { +#[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) +fn test_serde_event_v2_contract_resolution_expiration() { let j = json!( { - "siacoinInputs": [ + "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "index": { + "height": 190, + "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 334, + "type": "v2ContractResolution", + "data": { + "resolution": { + "parent": { + "id": "h:34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", + "leafIndex": 351, + "merkleProof": [ + "h:e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", + "h:70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", + "h:42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", + "h:4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", + "h:59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", + "h:f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", + "h:3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 179, + "expirationHeight": 189, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", + "hostSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" + } + }, + "type": "expiration", + "resolution": {} + }, + "siacoinElement": { + "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "leafIndex": 391, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 334 + }, + "missed": true + } + } + ); + + let _event = serde_json::from_value::(j).unwrap(); +} + +fn test_serde_event_v2_transaction() { + let j = json!( + { + "id": "h:5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", + "index": { + "height": 203, + "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 203, + "type": "v2Transaction", + "data": { + "siacoinInputs": [ { "parent": { - "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", - "leafIndex": 3, + "id": "h:78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", + "leafIndex": 421, "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + "h:f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", + "h:d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", + "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" ], "siacoinOutput": { - "value": "300000000000000000000000000000", + "value": "256394172736732570239334030000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "maturityHeight": 145 + "maturityHeight": 0 }, "satisfiedPolicy": { "policy": { @@ -537,33 +472,93 @@ fn test_v2_transaction_serde_basic_send() { "policy": { "timelock": 0, "publicKeys": [ - "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" + "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" ], "signaturesRequired": 1 } }, "signatures": [ - "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" + "sig:c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" ] } } - ], - "siacoinOutputs": [ + ], + "siacoinOutputs": [ { - "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "value": "10400000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, { - "value": "299000000000000000000000000000", + "value": "245994172736732570239334030000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } + ], + "minerFee": "0" + } + } + ); + test_serde!(Event, j); +} + +fn test_v2_transaction_serde_basic_send() { + let j = json!( + { + "siacoinInputs": [ + { + "parent": { + "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", + "leafIndex": 3, + "merkleProof": [ + "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + ], + "siacoinOutput": { + "value": "300000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 145 + }, + "satisfiedPolicy": { + "policy": { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" + ], + "signaturesRequired": 1 + } + }, + "signatures": [ + "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" + ] + } + } + ], + "siacoinOutputs": [ + { + "value": "1000000000000000000000000000", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + { + "value": "299000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + } ], "minerFee": "0" } ); let tx = serde_json::from_value::(j).unwrap(); - + let j2 = serde_json::to_value(&tx).unwrap().to_string(); let tx2 = serde_json::from_str::(&j2).unwrap(); assert_eq!(tx, tx2); } +} +} \ No newline at end of file From 21df8881363e6cc56263e2437a0929e0991951cb Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 12:01:02 -0400 Subject: [PATCH 080/233] wrap tests/transaction in cross_target_tests --- src/tests/transaction.rs | 1423 +++++++++++++++++++------------------- 1 file changed, 700 insertions(+), 723 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 0d630ce..e017775 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -1,737 +1,714 @@ -use crate::encoding::Encoder; -use crate::types::{Address, Hash256, Keypair, PublicKey, Signature, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, - SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, - StateElement, V2FileContract, V2FileContractElement, V2Transaction, spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, - UnlockCondition}; -use std::convert::TryFrom; -use std::str::FromStr; - -#[test] -fn test_siacoin_input_encode() { - let public_key = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); - - let vin = SiacoinInputV1 { - parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000") - .unwrap() - .into(), - unlock_condition, - }; - - let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_currency_encode_v1() { - let currency: Currency = 1.into(); - - let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_currency_encode_v2() { - let currency: Currency = 1.into(); - - let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_currency_encode_v1_max() { - let currency = Currency(u128::MAX); - - let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_currency_encode_v2_max() { - let currency = Currency(u128::MAX); - - let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_output_encode_v1() { - let vout = SiacoinOutput { - value: 1.into(), - address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") - .unwrap(), - }; - - let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); - let expected = Hash256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_output_encode_v2() { - let vout = SiacoinOutput { - value: 1.into(), - address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") - .unwrap(), - }; - - let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); - let expected = Hash256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_element_encode() { - let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), - }; - let siacoin_element = SiacoinElement { - state_element, - siacoin_output: SiacoinOutput { - value: 1.into(), - address: Address::from_str( - "addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a", +#[cfg(test)] +mod test { + use crate::encoding::Encoder; + use crate::types::{Address, Hash256, Keypair, PublicKey, Signature, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, + SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, + StateElement, V2FileContract, V2FileContractElement, V2Transaction, spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, + UnlockCondition}; + use std::convert::TryFrom; + use std::str::FromStr; + + cross_target_tests!{ + fn test_siacoin_input_encode() { + let public_key = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) - .unwrap(), - }, - maturity_height: 0, - }; - - let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = Hash256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_state_element_encode() { - let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), - }; - - let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_state_element_encode_null_merkle_proof() { - let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: None, - }; - - let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_input_encode_v1() { - let vin = SiacoinInputV1 { - parent_id: Hash256::default().into(), - unlock_condition: UnlockCondition::new(vec![], 0, 0), - }; - - let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_signature_encode() { - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let hash = Encoder::encode_and_hash(&signature); - let expected = Hash256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_public_key() { - let public_key = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let policy = SpendPolicy::PublicKey(public_key); - - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![signature], - preimages: vec![], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_hash_empty() { - let policy = SpendPolicy::Hash(Hash256::default()); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![], - preimages: vec![vec![]], // vec!(1u8, 2u8, 3u8, 4u8) - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); - assert_eq!(hash, expected); -} - -// Adding a signature to SatisfiedPolicy of PolicyHash should have no effect -#[test] -fn test_satisfied_policy_encode_hash_frivulous_signature() { - let policy = SpendPolicy::Hash(Hash256::default()); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec!(Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), - preimages: vec!(vec!(1u8, 2u8, 3u8, 4u8)), - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_hash() { - let policy = SpendPolicy::Hash(Hash256::default()); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_unlock_condition_standard() { - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - - let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); - - let policy = SpendPolicy::UnlockConditions(unlock_condition); - - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![signature], - preimages: vec![], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_unlock_condition_complex() { - let pubkey0 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey1 = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - let pubkey2 = PublicKey::from_bytes( - &hex::decode("BE043906FD42297BC0A03CAA6E773EF27FC644261C692D090181E704BE4A88C3").unwrap(), - ) - .unwrap(); - - let unlock_condition = UnlockCondition::new(vec![pubkey0, pubkey1, pubkey2], 77777777, 3); - - let policy = SpendPolicy::UnlockConditions(unlock_condition); - - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - let sig2 = Signature::from_bytes( - &hex::decode("482A2A905D7A6FC730387E06B45EA0CF259FCB219C9A057E539E705F60AC36D7079E26DAFB66ED4DBA9B9694B50BCA64F1D4CC4EBE937CE08A34BF642FAC1F0C").unwrap()).unwrap(); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![sig0, sig1, sig2], - preimages: vec![], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_threshold_simple() { - let sub_policy = SpendPolicy::Hash(Hash256::default()); - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![sub_policy], - }; - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_threshold_atomic_swap_success() { - let alice_pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let bob_pubkey = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - - let policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![signature], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { - let alice_pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let bob_pubkey = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - - let policy = spend_policy_atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let satisfied_policy = SatisfiedPolicy { - policy, - signatures: vec![signature], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], - }; - - let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_siacoin_input_encode_v2() { - let sub_policy = SpendPolicy::Hash(Hash256::default()); - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![sub_policy], - }; - - let satisfied_policy = SatisfiedPolicy { - policy: policy.clone(), - signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], - }; - - let vin = SiacoinInputV2 { - parent: SiacoinElement { - state_element: StateElement { - id: Hash256::default(), - leaf_index: 0, - merkle_proof: Some(vec![Hash256::default()]), - }, - siacoin_output: SiacoinOutput { + .unwrap(); + let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); + + let vin = SiacoinInputV1 { + parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000") + .unwrap() + .into(), + unlock_condition, + }; + + let hash = Encoder::encode_and_hash(&vin); + let expected = Hash256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_currency_encode_v1() { + let currency: Currency = 1.into(); + + let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); + let expected = Hash256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_currency_encode_v2() { + let currency: Currency = 1.into(); + + let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); + let expected = Hash256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_currency_encode_v1_max() { + let currency = Currency(u128::MAX); + + let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); + let expected = Hash256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_currency_encode_v2_max() { + let currency = Currency(u128::MAX); + + let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); + let expected = Hash256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_output_encode_v1() { + let vout = SiacoinOutput { value: 1.into(), - address: policy.address(), - }, - maturity_height: 0, - }, - satisfied_policy, - }; - - let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_attestation_encode() { - let public_key = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - - let attestation = Attestation { - public_key, - key: "HostAnnouncement".to_string(), - value: vec![1u8, 2u8, 3u8, 4u8], - signature, - }; - - let hash = Encoder::encode_and_hash(&attestation); - let expected = Hash256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_file_contract_v2_encode() { - let pubkey0 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey1 = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - - let address0 = Address::standard_address_v1(&pubkey0); - let address1 = Address::standard_address_v1(&pubkey1); - - let vout0 = SiacoinOutput { - value: 1.into(), - address: address0, - }; - let vout1 = SiacoinOutput { - value: 1.into(), - address: address1, - }; - - let file_contract_v2 = V2FileContract { - filesize: 1, - file_merkle_root: Hash256::default(), - proof_height: 1, - expiration_height: 1, - renter_output: vout0, - host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), - renter_public_key: pubkey0, - host_public_key: pubkey1, - revision_number: 1, - renter_signature: sig0, - host_signature: sig1, - }; - - let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = Hash256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_file_contract_element_v2_encode() { - let pubkey0 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey1 = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - - let address0 = Address::standard_address_v1(&pubkey0); - let address1 = Address::standard_address_v1(&pubkey1); - - let vout0 = SiacoinOutput { - value: 1.into(), - address: address0, - }; - let vout1 = SiacoinOutput { - value: 1.into(), - address: address1, - }; - - let file_contract_v2 = V2FileContract { - filesize: 1, - file_merkle_root: Hash256::default(), - proof_height: 1, - expiration_height: 1, - renter_output: vout0, - host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), - renter_public_key: pubkey0, - host_public_key: pubkey1, - revision_number: 1, - renter_signature: sig0, - host_signature: sig1, - }; - - let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), - }; - - let file_contract_element_v2 = V2FileContractElement { - state_element, - v2_file_contract: file_contract_v2, - }; - - let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = Hash256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_file_contract_revision_v2_encode() { - let pubkey0 = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let pubkey1 = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - - let address0 = Address::standard_address_v1(&pubkey0); - let address1 = Address::standard_address_v1(&pubkey1); - - let vout0 = SiacoinOutput { - value: 1.into(), - address: address0, - }; - let vout1 = SiacoinOutput { - value: 1.into(), - address: address1, - }; - - let file_contract_v2 = V2FileContract { - filesize: 1, - file_merkle_root: Hash256::default(), - proof_height: 1, - expiration_height: 1, - renter_output: vout0, - host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), - renter_public_key: pubkey0, - host_public_key: pubkey1, - revision_number: 1, - renter_signature: sig0, - host_signature: sig1, - }; - - let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), - }; - - let file_contract_element_v2 = V2FileContractElement { - state_element, - v2_file_contract: file_contract_v2.clone(), - }; - - let file_contract_revision_v2 = FileContractRevisionV2 { - parent: file_contract_element_v2, - revision: file_contract_v2, - }; - - let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = Hash256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_v2_transaction_sig_hash() { - let j = json!( - { - "siacoinInputs": [ - { - "parent": { - "id": "h:b49cba94064a92a75bf8c6f9d32ab18f38bfb14a2252e3e117d04da89d536f29", - "leafIndex": 302, - "merkleProof": [ - "h:6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", - "h:eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", - "h:04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", - "h:16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", - "h:1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", - "h:31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" - ], - "siacoinOutput": { - "value": "288594172736732570239334030000", - "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" - }, - "maturityHeight": 0 + address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") + .unwrap(), + }; + + let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); + let expected = Hash256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_output_encode_v2() { + let vout = SiacoinOutput { + value: 1.into(), + address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") + .unwrap(), + }; + + let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); + let expected = Hash256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_element_encode() { + let state_element = StateElement { + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + leaf_index: 1, + merkle_proof: Some(vec![ + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + ]), + }; + let siacoin_element = SiacoinElement { + state_element, + siacoin_output: SiacoinOutput { + value: 1.into(), + address: Address::from_str( + "addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a", + ) + .unwrap(), + }, + maturity_height: 0, + }; + + let hash = Encoder::encode_and_hash(&siacoin_element); + let expected = Hash256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); + assert_eq!(hash, expected); + } + + fn test_state_element_encode() { + let state_element = StateElement { + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + leaf_index: 1, + merkle_proof: Some(vec![ + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + ]), + }; + + let hash = Encoder::encode_and_hash(&state_element); + let expected = Hash256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); + assert_eq!(hash, expected); + } + + fn test_state_element_encode_null_merkle_proof() { + let state_element = StateElement { + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + leaf_index: 1, + merkle_proof: None, + }; + + let hash = Encoder::encode_and_hash(&state_element); + let expected = Hash256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_input_encode_v1() { + let vin = SiacoinInputV1 { + parent_id: Hash256::default().into(), + unlock_condition: UnlockCondition::new(vec![], 0, 0), + }; + + let hash = Encoder::encode_and_hash(&vin); + let expected = Hash256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); + assert_eq!(hash, expected); + } + + fn test_signature_encode() { + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let hash = Encoder::encode_and_hash(&signature); + let expected = Hash256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_public_key() { + let public_key = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let policy = SpendPolicy::PublicKey(public_key); + + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![signature], + preimages: vec![], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_hash_empty() { + let policy = SpendPolicy::Hash(Hash256::default()); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![], + preimages: vec![vec![]], // vec!(1u8, 2u8, 3u8, 4u8) + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); + assert_eq!(hash, expected); + } + + // Adding a signature to SatisfiedPolicy of PolicyHash should have no effect + fn test_satisfied_policy_encode_hash_frivulous_signature() { + let policy = SpendPolicy::Hash(Hash256::default()); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec!(Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), + preimages: vec!(vec!(1u8, 2u8, 3u8, 4u8)), + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_hash() { + let policy = SpendPolicy::Hash(Hash256::default()); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![], + preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_unlock_condition_standard() { + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); + + let policy = SpendPolicy::UnlockConditions(unlock_condition); + + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![signature], + preimages: vec![], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_unlock_condition_complex() { + let pubkey0 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey1 = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + let pubkey2 = PublicKey::from_bytes( + &hex::decode("BE043906FD42297BC0A03CAA6E773EF27FC644261C692D090181E704BE4A88C3").unwrap(), + ) + .unwrap(); + + let unlock_condition = UnlockCondition::new(vec![pubkey0, pubkey1, pubkey2], 77777777, 3); + + let policy = SpendPolicy::UnlockConditions(unlock_condition); + + let sig0 = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::from_bytes( + &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + let sig2 = Signature::from_bytes( + &hex::decode("482A2A905D7A6FC730387E06B45EA0CF259FCB219C9A057E539E705F60AC36D7079E26DAFB66ED4DBA9B9694B50BCA64F1D4CC4EBE937CE08A34BF642FAC1F0C").unwrap()).unwrap(); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![sig0, sig1, sig2], + preimages: vec![], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_threshold_simple() { + let sub_policy = SpendPolicy::Hash(Hash256::default()); + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![sub_policy], + }; + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![], + preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_threshold_atomic_swap_success() { + let alice_pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let bob_pubkey = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + + let policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![signature], + preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); + assert_eq!(hash, expected); + } + + fn test_satisfied_policy_encode_threshold_atomic_swap_refund() { + let alice_pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let bob_pubkey = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + + let policy = spend_policy_atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let satisfied_policy = SatisfiedPolicy { + policy, + signatures: vec![signature], + preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + }; + + let hash = Encoder::encode_and_hash(&satisfied_policy); + let expected = Hash256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); + assert_eq!(hash, expected); + } + + fn test_siacoin_input_encode_v2() { + let sub_policy = SpendPolicy::Hash(Hash256::default()); + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![sub_policy], + }; + + let satisfied_policy = SatisfiedPolicy { + policy: policy.clone(), + signatures: vec![], + preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + }; + + let vin = SiacoinInputV2 { + parent: SiacoinElement { + state_element: StateElement { + id: Hash256::default(), + leaf_index: 0, + merkle_proof: Some(vec![Hash256::default()]), + }, + siacoin_output: SiacoinOutput { + value: 1.into(), + address: policy.address(), }, - "satisfiedPolicy": { - "policy": { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:7931b69fe8888e354d601a778e31bfa97fa89dc6f625cd01cc8aa28046e557e7" + maturity_height: 0, + }, + satisfied_policy, + }; + + let hash = Encoder::encode_and_hash(&vin); + let expected = Hash256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); + assert_eq!(hash, expected); + } + + fn test_attestation_encode() { + let public_key = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let signature = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + + let attestation = Attestation { + public_key, + key: "HostAnnouncement".to_string(), + value: vec![1u8, 2u8, 3u8, 4u8], + signature, + }; + + let hash = Encoder::encode_and_hash(&attestation); + let expected = Hash256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); + assert_eq!(hash, expected); + } + + fn test_file_contract_v2_encode() { + let pubkey0 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey1 = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let sig0 = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::from_bytes( + &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); + + let vout0 = SiacoinOutput { + value: 1.into(), + address: address0, + }; + let vout1 = SiacoinOutput { + value: 1.into(), + address: address1, + }; + + let file_contract_v2 = V2FileContract { + filesize: 1, + file_merkle_root: Hash256::default(), + proof_height: 1, + expiration_height: 1, + renter_output: vout0, + host_output: vout1, + missed_host_value: 1.into(), + total_collateral: 1.into(), + renter_public_key: pubkey0, + host_public_key: pubkey1, + revision_number: 1, + renter_signature: sig0, + host_signature: sig1, + }; + + let hash = Encoder::encode_and_hash(&file_contract_v2); + let expected = Hash256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); + assert_eq!(hash, expected); + } + + fn test_file_contract_element_v2_encode() { + let pubkey0 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey1 = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let sig0 = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::from_bytes( + &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); + + let vout0 = SiacoinOutput { + value: 1.into(), + address: address0, + }; + let vout1 = SiacoinOutput { + value: 1.into(), + address: address1, + }; + + let file_contract_v2 = V2FileContract { + filesize: 1, + file_merkle_root: Hash256::default(), + proof_height: 1, + expiration_height: 1, + renter_output: vout0, + host_output: vout1, + missed_host_value: 1.into(), + total_collateral: 1.into(), + renter_public_key: pubkey0, + host_public_key: pubkey1, + revision_number: 1, + renter_signature: sig0, + host_signature: sig1, + }; + + let state_element = StateElement { + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + leaf_index: 1, + merkle_proof: Some(vec![ + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + ]), + }; + + let file_contract_element_v2 = V2FileContractElement { + state_element, + v2_file_contract: file_contract_v2, + }; + + let hash = Encoder::encode_and_hash(&file_contract_element_v2); + let expected = Hash256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); + assert_eq!(hash, expected); + } + + fn test_file_contract_revision_v2_encode() { + let pubkey0 = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let pubkey1 = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let sig0 = Signature::from_bytes( + &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::from_bytes( + &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + + let address0 = Address::standard_address_v1(&pubkey0); + let address1 = Address::standard_address_v1(&pubkey1); + + let vout0 = SiacoinOutput { + value: 1.into(), + address: address0, + }; + let vout1 = SiacoinOutput { + value: 1.into(), + address: address1, + }; + + let file_contract_v2 = V2FileContract { + filesize: 1, + file_merkle_root: Hash256::default(), + proof_height: 1, + expiration_height: 1, + renter_output: vout0, + host_output: vout1, + missed_host_value: 1.into(), + total_collateral: 1.into(), + renter_public_key: pubkey0, + host_public_key: pubkey1, + revision_number: 1, + renter_signature: sig0, + host_signature: sig1, + }; + + let state_element = StateElement { + id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + leaf_index: 1, + merkle_proof: Some(vec![ + Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + ]), + }; + + let file_contract_element_v2 = V2FileContractElement { + state_element, + v2_file_contract: file_contract_v2.clone(), + }; + + let file_contract_revision_v2 = FileContractRevisionV2 { + parent: file_contract_element_v2, + revision: file_contract_v2, + }; + + let hash = Encoder::encode_and_hash(&file_contract_revision_v2); + let expected = Hash256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); + assert_eq!(hash, expected); + } + + fn test_v2_transaction_sig_hash() { + let j = json!( + { + "siacoinInputs": [ + { + "parent": { + "id": "h:b49cba94064a92a75bf8c6f9d32ab18f38bfb14a2252e3e117d04da89d536f29", + "leafIndex": 302, + "merkleProof": [ + "h:6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", + "h:eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", + "h:04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", + "h:16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", + "h:1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", + "h:31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" ], - "signaturesRequired": 1 + "siacoinOutput": { + "value": "288594172736732570239334030000", + "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" + }, + "maturityHeight": 0 + }, + "satisfiedPolicy": { + "policy": { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:7931b69fe8888e354d601a778e31bfa97fa89dc6f625cd01cc8aa28046e557e7" + ], + "signaturesRequired": 1 + } + }, + "signatures": [ + "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902" + ] } + } + ], + "siacoinOutputs": [ + { + "value": "1000000000000000000000000000", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, - "signatures": [ - "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902" - ] - } - } - ], - "siacoinOutputs": [ - { - "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - { - "value": "287594172736732570239334030000", - "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" + { + "value": "287594172736732570239334030000", + "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" + } + ], + "minerFee": "0" } - ], - "minerFee": "0" + ); + + let tx = serde_json::from_value::(j).unwrap(); + let hash = tx.input_sig_hash(); + let expected = Hash256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); + assert_eq!(hash, expected); } - ); - - let tx = serde_json::from_value::(j).unwrap(); - let hash = tx.input_sig_hash(); - let expected = Hash256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); - assert_eq!(hash, expected); -} - -#[test] -fn test_v2_transaction_signing() { - let j = json!( - { - "siacoinInputs": [ + + fn test_v2_transaction_signing() { + let j = json!( { - "parent": { - "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", - "leafIndex": 3, - "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" - ], - "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 145 - }, - "satisfiedPolicy": { - "policy": { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" + "siacoinInputs": [ + { + "parent": { + "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", + "leafIndex": 3, + "merkleProof": [ + "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" ], - "signaturesRequired": 1 + "siacoinOutput": { + "value": "300000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 145 + }, + "satisfiedPolicy": { + "policy": { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" + ], + "signaturesRequired": 1 + } + }, + "signatures": [ + "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" + ] } + } + ], + "siacoinOutputs": [ + { + "value": "1000000000000000000000000000", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, - "signatures": [ - "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" - ] - } - } - ], - "siacoinOutputs": [ - { - "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - { - "value": "299000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + { + "value": "299000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + } + ], + "minerFee": "0" } - ], - "minerFee": "0" + ); + let tx = serde_json::from_value::(j).unwrap(); + let keypair = Keypair::from_private_bytes( + &hex::decode("0100000000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let sig_hash = tx.input_sig_hash(); + + // test that we can correctly regenerate the signature + let sig: Signature = keypair.sign(&sig_hash.0); + assert_eq!(tx.siacoin_inputs[0].satisfied_policy.signatures[0], sig); } - ); - let tx = serde_json::from_value::(j).unwrap(); - let keypair = Keypair::from_private_bytes( - &hex::decode("0100000000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let sig_hash = tx.input_sig_hash(); - - // test that we can correctly regenerate the signature - let sig: Signature = keypair.sign(&sig_hash.0); - assert_eq!(tx.siacoin_inputs[0].satisfied_policy.signatures[0], sig); -} + } +} \ No newline at end of file From 530c7d0c39ddd7cb1481dc805818ae46a8eafee3 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 12:05:09 -0400 Subject: [PATCH 081/233] wrap tests/spend_policy in cross_target_tests --- src/tests/spend_policy.rs | 312 +++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 156 deletions(-) diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 14d3148..1a95e3b 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,164 +1,164 @@ -use crate::types::{Address, Hash256, PublicKey, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; -use std::convert::TryFrom; -use std::str::FromStr; - -#[test] -fn test_serde_spend_policy_above() { - let j = json!( - { - "type": "above", - "policy": 100 - } - ); - - let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::Above(100); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_after() { - let j = json!( - { - "type": "after", - "policy": 200 - } - ); - - let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::After(200); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_public_key() { - let j = json!( - { - "type": "pk", - "policy": "ed25519:0102030000000000000000000000000000000000000000000000000000000000" - } - ); - let pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::PublicKey(pubkey); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_hash() { - let j = json!( - { - "type": "h", - "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" - } - ); - let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::Hash(hash); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_opaque() { - let j = json!( - { - "type": "opaque", - "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" - } - ); - let address = - Address::from_str("addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a").unwrap(); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::Opaque(address); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_threshold() { - let alice_pubkey = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); - let bob_pubkey = PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap(); - - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let spend_policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); - - let j = json!( - { - "type": "thresh", - "policy": { - "n": 1, - "of": [ - { - "type": "thresh", - "policy": { - "n": 2, - "of": [ - { - "type": "pk", - "policy": "ed25519:0102030000000000000000000000000000000000000000000000000000000000" - }, - { - "type": "h", - "policy": "h:0100000000000000000000000000000000000000000000000000000000000000" - } - ] - } - }, - { - "type": "opaque", - "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" - } - ] + + +#[cfg(test)] +mod test { + use crate::types::{Address, Hash256, PublicKey, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; + use std::convert::TryFrom; + use std::str::FromStr; + + cross_target_tests! { + fn test_serde_spend_policy_above() { + let j = json!( + { + "type": "above", + "policy": 100 } + ); + + let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::Above(100); + + assert_eq!(spend_policy, spend_policy_deser); } - ); - - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); - - assert_eq!(spend_policy, spend_policy_deser); -} - -#[test] -fn test_serde_spend_policy_unlock_conditions_standard() { - let j = json!( - { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:0102030000000000000000000000000000000000000000000000000000000000" - ], - "signaturesRequired": 1 + + fn test_serde_spend_policy_after() { + let j = json!( + { + "type": "after", + "policy": 200 } + ); + + let spend_policy_deser = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::After(200); + + assert_eq!(spend_policy, spend_policy_deser); } - ); - let public_key = PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ) - .unwrap(); + fn test_serde_spend_policy_public_key() { + let j = json!( + { + "type": "pk", + "policy": "ed25519:0102030000000000000000000000000000000000000000000000000000000000" + } + ); + let pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::PublicKey(pubkey); + + assert_eq!(spend_policy, spend_policy_deser); + } - let uc = UnlockCondition { - unlock_keys: vec![UnlockKey::Ed25519(public_key)], - timelock: 0, - signatures_required: 1, - }; + fn test_serde_spend_policy_hash() { + let j = json!( + { + "type": "h", + "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" + } + ); + let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::Hash(hash); - let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); - let spend_policy = SpendPolicy::UnlockConditions(uc); + assert_eq!(spend_policy, spend_policy_deser); + } - assert_eq!(spend_policy, spend_policy_deser); -} + fn test_serde_spend_policy_opaque() { + let j = json!( + { + "type": "opaque", + "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" + } + ); + let address = + Address::from_str("addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a").unwrap(); + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::Opaque(address); + + assert_eq!(spend_policy, spend_policy_deser); + } + + fn test_serde_spend_policy_threshold() { + let alice_pubkey = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + let bob_pubkey = PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap(); + + let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let spend_policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + + let j = json!( + { + "type": "thresh", + "policy": { + "n": 1, + "of": [ + { + "type": "thresh", + "policy": { + "n": 2, + "of": [ + { + "type": "pk", + "policy": "ed25519:0102030000000000000000000000000000000000000000000000000000000000" + }, + { + "type": "h", + "policy": "h:0100000000000000000000000000000000000000000000000000000000000000" + } + ] + } + }, + { + "type": "opaque", + "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" + } + ] + } + } + ); + + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + + assert_eq!(spend_policy, spend_policy_deser); + } + + fn test_serde_spend_policy_unlock_conditions_standard() { + let j = json!( + { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:0102030000000000000000000000000000000000000000000000000000000000" + ], + "signaturesRequired": 1 + } + } + ); + + let public_key = PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ) + .unwrap(); + + let uc = UnlockCondition { + unlock_keys: vec![UnlockKey::Ed25519(public_key)], + timelock: 0, + signatures_required: 1, + }; + + let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); + let spend_policy = SpendPolicy::UnlockConditions(uc); + + assert_eq!(spend_policy, spend_policy_deser); + } + } +} \ No newline at end of file From e5b372a7d0ea4feea02723e1db12e64b05892332 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 22 Sep 2024 12:24:53 -0400 Subject: [PATCH 082/233] replace standalone atomic_swap functions with SpendPolicy methods --- src/tests/spend_policy.rs | 4 +- src/tests/transaction.rs | 6 +-- src/types/spend_policy.rs | 78 ++++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 1a95e3b..5d658bc 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -2,7 +2,7 @@ #[cfg(test)] mod test { - use crate::types::{Address, Hash256, PublicKey, spend_policy_atomic_swap_success, SpendPolicy, UnlockCondition, UnlockKey}; + use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition, UnlockKey}; use std::convert::TryFrom; use std::str::FromStr; @@ -92,7 +92,7 @@ mod test { .unwrap(); let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let spend_policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let spend_policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let j = json!( { diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index e017775..45829ce 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -3,7 +3,7 @@ mod test { use crate::encoding::Encoder; use crate::types::{Address, Hash256, Keypair, PublicKey, Signature, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, - StateElement, V2FileContract, V2FileContractElement, V2Transaction, spend_policy_atomic_swap_refund, spend_policy_atomic_swap_success, SpendPolicy, + StateElement, V2FileContract, V2FileContractElement, V2Transaction, SpendPolicy, UnlockCondition}; use std::convert::TryFrom; use std::str::FromStr; @@ -313,7 +313,7 @@ mod test { let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let policy = spend_policy_atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); @@ -340,7 +340,7 @@ mod test { let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let policy = spend_policy_atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let policy = SpendPolicy::atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index f8322c9..7650e28 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -176,48 +176,50 @@ impl SatisfyPolicy for () { } } -pub fn spend_policy_atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { - let policy_after = SpendPolicy::After(lock_time); - let policy_hash = SpendPolicy::Hash(hash); - - let policy_success = SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::PublicKey(alice), policy_hash], - }; - - let policy_refund = SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::PublicKey(bob), policy_after], - }; - - SpendPolicy::Threshold { - n: 1, - of: vec![policy_success, policy_refund], +impl SpendPolicy { + pub fn atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { + let policy_after = SpendPolicy::After(lock_time); + let policy_hash = SpendPolicy::Hash(hash); + + let policy_success = SpendPolicy::Threshold { + n: 2, + of: vec![SpendPolicy::PublicKey(alice), policy_hash], + }; + + let policy_refund = SpendPolicy::Threshold { + n: 2, + of: vec![SpendPolicy::PublicKey(bob), policy_after], + }; + + SpendPolicy::Threshold { + n: 1, + of: vec![policy_success, policy_refund], + } } -} -pub fn spend_policy_atomic_swap_success( - alice: PublicKey, - bob: PublicKey, - lock_time: u64, - hash: Hash256, -) -> SpendPolicy { - match spend_policy_atomic_swap(alice, bob, lock_time, hash) { - SpendPolicy::Threshold { n, mut of } => { - of[1] = of[1].opacify(); - SpendPolicy::Threshold { n, of } - }, - _ => unreachable!(), + pub fn atomic_swap_success( + alice: PublicKey, + bob: PublicKey, + lock_time: u64, + hash: Hash256, + ) -> Self { + match Self::atomic_swap(alice, bob, lock_time, hash) { + SpendPolicy::Threshold { n, mut of } => { + of[1] = of[1].opacify(); + SpendPolicy::Threshold { n, of } + }, + _ => unreachable!(), + } } -} -pub fn spend_policy_atomic_swap_refund(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> SpendPolicy { - match spend_policy_atomic_swap(alice, bob, lock_time, hash) { - SpendPolicy::Threshold { n, mut of } => { - of[0] = of[0].opacify(); - SpendPolicy::Threshold { n, of } - }, - _ => unreachable!(), + pub fn atomic_swap_refund(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { + match Self::atomic_swap(alice, bob, lock_time, hash) { + SpendPolicy::Threshold { n, mut of } => { + of[0] = of[0].opacify(); + SpendPolicy::Threshold { n, of } + }, + _ => unreachable!(), + } } } From 2d5cebe8c46498d79571099891d94503bac0fd69 Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 24 Sep 2024 09:34:19 -0400 Subject: [PATCH 083/233] init atomic_swap mod WIP --- src/types.rs | 3 + src/types/atomic_swap.rs | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/types/atomic_swap.rs diff --git a/src/types.rs b/src/types.rs index cea420f..577272d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -28,6 +28,9 @@ pub use transaction::*; mod specifier; pub use specifier::*; +mod atomic_swap; +pub use atomic_swap::*; + const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs new file mode 100644 index 0000000..ca4c1d1 --- /dev/null +++ b/src/types/atomic_swap.rs @@ -0,0 +1,116 @@ +use crate::types::{PublicKey, SpendPolicy}; + +use std::marker::PhantomData; +use thiserror::Error; + +/* +SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Above(), + SpendPolicy::PublicKey() + ] + }, + ] +} +*/ + +pub enum AtomicSwapError { + #[error("invalid time threshold: {:?}", 0)] + InvalidTimeThreshold(SpendPolicy), + #[error("invalid hash threshold: {:?}", 0)] + InvalidHashThreshold(SpendPolicy), + #[error("invalid refund path: {:?}", 0)] + InvalidHashThreshold(SpendPolicy), +} + +/// Represents an atomic swap contract. +/// PhantomData is used to enforce type safety on the structure of the SpendPolicy. +pub struct AtomicSwap { + policy: SpendPolicy, + _marker: PhantomData, +} + +/// Represents one of the Threshold components of an atomic swap contract. +/// PhantomData is used to enforce type safety on the structure of the SpendPolicy. +pub struct AtomicSwapComponent { + policy: SpendPolicy, + _marker: PhantomData, +} + + +/// The full atomic swap contract. +/// This is opacified(hashed) and used as the SpendPolicy for the locked transaction output. +/// This is used only to create outputs, never inputs. +struct Full; + +/// The success branch of the atomic swap contract. +/// The refund path is opacified and resulting SpendPolicy is used in transaction input's SatifiedPolicy +/// This is used only to create inputs, never outputs. +struct SuccessBranch; + +/// The refund branch of the atomic swap contract. +/// The success path is opacified and resulting SpendPolicy is used in transaction input's SatisfiedPolicy +/// This is used only to create inputs, never outputs. +struct RefundBranch; + +/// 2 of 2 threshold of SpendPolicy::Hash and SpendPolicy::PublicKey +struct HashPublicKey2of2; + +impl AtomicSwapComponent { + pub fn new(policy: SpendPolicy) -> Result { + if Self::is_valid_hash_threshold(&policy) { + Ok(Self { + policy, + _marker: PhantomData, + }) + } else { + Err(AtomicSwapError::InvalidHashThreshold(policy)) + } + } + + fn is_valid_hash_threshold(policy: &SpendPolicy) -> bool { + match policy { + SpendPolicy::Threshold{ n: 2, of: [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] } => true, + _ => false, + } + } +} + +/// The time threshold branch of the atomic swap contract. +/// 2 of 2 threshold of SpendPolicy::After and SpendPolicy::PublicKey +struct TimeThreshold; + +impl AtomicSwapComponent { + pub fn new(policy: SpendPolicy) -> Result { + if Self::is_valid_time_threshold(&policy) { + Ok(Self { + policy, + _marker: PhantomData, + }) + } else { + Err(AtomicSwapError::InvalidTimeThreshold(policy)) + } + } + + fn is_valid_time_threshold(policy: &SpendPolicy) -> bool { + match policy { + SpendPolicy::Threshold{ n: 2, of: [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] } => true, + _ => false, + } + } +} + + + + From 9a17682d0b62f63b9f2ad1d5b355f33ac2e18f99 Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 24 Sep 2024 16:09:17 -0400 Subject: [PATCH 084/233] init AtomicSwap types and tests --- src/types/atomic_swap.rs | 483 +++++++++++++++++++++++++++++++++------ 1 file changed, 410 insertions(+), 73 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index ca4c1d1..4566abc 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -1,116 +1,453 @@ -use crate::types::{PublicKey, SpendPolicy}; +use crate::types::SpendPolicy; use std::marker::PhantomData; use thiserror::Error; /* -SpendPolicy::Threshold { - n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::After(), - SpendPolicy::PublicKey() - ] - }, - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::Above(), - SpendPolicy::PublicKey() - ] - }, - ] +The full representation of the atomic swap contract is as follows: + SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(), + SpendPolicy::PublicKey() + ] + }, + ] + } + +In English, the above specifies that: + - Alice can spend the UTXO if: + - Alice provides the preimage of the SHA256 hash specified in the UTXO (the secret) + - Alice provides a valid signature + - Bob can spend the UTXO if: + - the current time is greater than the specified timestamp + - Bob provides a valid signature + +To lock funds in such a contract, we generate the address(see SpendPolicy::address) of the above SpendPolicy and use this Address in a transaction output. + +The resulting UTXO can then be spent by either Alice or Bob by meeting the conditions specified above. + +It is only neccesary to reveal the path that will be satisfied. The other path will be opacified(see SpendPolicy::opacify) and replaced with SpendPolicy::Opaque(). + +Alice can spend the UTXO by providing a signature, the secret and revealing the relevant path within the full SpendPolicy. Alice can construct the following SatisfiedPolicy to spend the UTXO: + +SatisfiedPolicy { + policy: SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Opaque(), + ] + }, + signatures: vec![], + preimages: vec![] } + +Similarly, Bob can spend the UTXO with the following SatisfiedPolicy assuming he waits until the timestamp has passed: + +SatisfiedPolicy { + policy: SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Opaque(), + ] + }, + signatures: vec![], + preimages: vec![] +} + */ +#[derive(Debug, Error)] pub enum AtomicSwapError { - #[error("invalid time threshold: {:?}", 0)] - InvalidTimeThreshold(SpendPolicy), - #[error("invalid hash threshold: {:?}", 0)] - InvalidHashThreshold(SpendPolicy), - #[error("invalid refund path: {:?}", 0)] - InvalidHashThreshold(SpendPolicy), + #[error("invalid atomic swap, invalid hash component: {}", .0)] + InvalidHashComponent(ComponentError), + #[error("invalid atomic swap, invalid time component: {}", .0)] + InvalidTimeComponent(ComponentError), + #[error("invalid atomic swap, wrong n:{} policy: {:?}", n, policy)] + InvalidN{ policy: SpendPolicy, n : u8 }, + #[error("invalid atomic swap, wrong m:{} policy: {:?}", m, policy)] + InvalidM{ policy: SpendPolicy, m : usize }, + #[error("invalid atomic swap, not a threshold: {:?}", .0)] + InvalidSpendPolicyVariant(SpendPolicy), } /// Represents an atomic swap contract. -/// PhantomData is used to enforce type safety on the structure of the SpendPolicy. +/// PhantomData is used to enforce type safety on the structure of a SpendPolicy which is a complex recursive structure. +#[derive(Debug)] pub struct AtomicSwap { - policy: SpendPolicy, + pub policy: SpendPolicy, _marker: PhantomData, } -/// Represents one of the Threshold components of an atomic swap contract. +/// The full atomic swap contract. +/// SpendPolicy:address is used to generate the address of the contract. +/// Funds can then be locked in the contract by creating a transaction output with this address. +/// This is used only to create outputs, never inputs. +#[derive(Debug)] +struct Contract; + +impl AtomicSwap { + pub fn new(policy: SpendPolicy) -> Result { + Self::is_valid(&policy).map(|_| Self { + policy, + _marker: PhantomData, + }) + } + + fn is_valid(policy: &SpendPolicy) -> Result<(), AtomicSwapError> { + match policy { + SpendPolicy::Threshold { + n: 1, + of + } if of.len() == 2 => { + AtomicSwapComponent::::is_valid(&of[0]).map_err(AtomicSwapError::InvalidHashComponent)?; + AtomicSwapComponent::::is_valid(&of[1]).map_err(AtomicSwapError::InvalidTimeComponent)?; + Ok(()) + }, + SpendPolicy::Threshold { n: 1, of } => Err(AtomicSwapError::InvalidM{ policy: policy.clone(), m: of.len() }), + SpendPolicy::Threshold { n, of: _ } => Err(AtomicSwapError::InvalidN{ policy: policy.clone(), n: n.clone() }), + _ => Err(AtomicSwapError::InvalidSpendPolicyVariant(policy.clone())), + } + } +} + +/// Represents a fulfillment path of the atomic swap contract. ie, HashLock or TimeLock. /// PhantomData is used to enforce type safety on the structure of the SpendPolicy. +#[derive(Debug)] pub struct AtomicSwapComponent { - policy: SpendPolicy, + pub policy: SpendPolicy, _marker: PhantomData, } +#[derive(Debug, Error)] +pub enum ComponentError { + #[error("invalid hash lock component, hash lock path: {:?}", .0)] + HashLockInvalidThresholdStructure(SpendPolicy), + #[error("invalid hash lock component, not a threshold: {:?}", .0)] + HashLockInvalidSpendPolicyVariant(SpendPolicy), + #[error("invalid hash lock component, wrong n:{} policy: {:?}", n, policy)] + HashLockInvalidN{ policy: SpendPolicy, n : u8 }, + #[error("invalid hash lock component, wrong m:{} policy: {:?}", m, policy)] + HashLockInvalidM{ policy: SpendPolicy, m : usize }, + #[error("invalid time lock component, time lock path: {:?}", .0)] + TimeLockInvalidThresholdStructure(SpendPolicy), + #[error("invalid time lock component, not a threshold: {:?}", .0)] + TimeLockInvalidSpendPolicyVariant(SpendPolicy), + #[error("invalid time lock component, wrong n:{} policy: {:?}", n, policy)] + TimeLockInvalidN{ policy: SpendPolicy, n : u8 }, + #[error("invalid time lock component, wrong m:{} policy: {:?}", m, policy)] + TimeLockInvalidM{ policy: SpendPolicy, m : usize }, +} -/// The full atomic swap contract. -/// This is opacified(hashed) and used as the SpendPolicy for the locked transaction output. -/// This is used only to create outputs, never inputs. -struct Full; +/// The hash locked threshold path of the atomic swap contract. +/// 2 of 2 threshold of: +/// SpendPolicy::Hash() and SpendPolicy::PublicKey() +/// where: +/// secret_hash == sha256(secret) +/// fulfillment conditions: +/// - signature from participant's public key +/// - sha256(secret) == hash +/// - length(secret) == 32 +#[derive(Debug)] +struct HashLockPath; -/// The success branch of the atomic swap contract. -/// The refund path is opacified and resulting SpendPolicy is used in transaction input's SatifiedPolicy -/// This is used only to create inputs, never outputs. -struct SuccessBranch; +impl AtomicSwapComponent { + pub fn new(policy: SpendPolicy) -> Result { + Self::is_valid(&policy).map(|_| Self { + policy, + _marker: PhantomData, + }) + } -/// The refund branch of the atomic swap contract. -/// The success path is opacified and resulting SpendPolicy is used in transaction input's SatisfiedPolicy -/// This is used only to create inputs, never outputs. -struct RefundBranch; + fn is_valid(policy: &SpendPolicy) -> Result<(), ComponentError> { + match policy { + SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { + match of.as_slice() { + [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] => Ok(()), + _ => Err(ComponentError::HashLockInvalidThresholdStructure(policy.clone())), + } + }, + SpendPolicy::Threshold{ n: 2, of } => Err(ComponentError::HashLockInvalidM{ policy: policy.clone(), m: of.len() }), + SpendPolicy::Threshold{ n, of: _ } => Err(ComponentError::HashLockInvalidN{ policy: policy.clone(), n: n.clone() }), + _ => Err(ComponentError::HashLockInvalidSpendPolicyVariant(policy.clone())), + } + } +} -/// 2 of 2 threshold of SpendPolicy::Hash and SpendPolicy::PublicKey -struct HashPublicKey2of2; +/// The time based threshold path of the atomic swap contract. +/// 2 of 2 threshold of SpendPolicy::After(timestamp) and SpendPolicy::PublicKey(participant's public key) +/// fulfillment conditions: +/// - signature from participant's public key +/// - timestamp has passed +#[derive(Debug)] +struct TimeLockPath; -impl AtomicSwapComponent { - pub fn new(policy: SpendPolicy) -> Result { - if Self::is_valid_hash_threshold(&policy) { - Ok(Self { - policy, - _marker: PhantomData, - }) - } else { - Err(AtomicSwapError::InvalidHashThreshold(policy)) - } +impl AtomicSwapComponent { + pub fn new(policy: SpendPolicy) -> Result { + Self::is_valid(&policy).map(|_| Self { + policy, + _marker: PhantomData, + }) } - fn is_valid_hash_threshold(policy: &SpendPolicy) -> bool { + fn is_valid(policy: &SpendPolicy) -> Result<(), ComponentError> { match policy { - SpendPolicy::Threshold{ n: 2, of: [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] } => true, - _ => false, + SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { + match of.as_slice() { + [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] => Ok(()), + _ => Err(ComponentError::TimeLockInvalidThresholdStructure(policy.clone())), + } + }, + SpendPolicy::Threshold{ n: 2, of } => Err(ComponentError::TimeLockInvalidM{ policy: policy.clone(), m: of.len() }), + SpendPolicy::Threshold{ n, of: _ } => Err(ComponentError::TimeLockInvalidN{ policy: policy.clone(), n: n.clone() }), + _ => Err(ComponentError::TimeLockInvalidSpendPolicyVariant(policy.clone())), } } } -/// The time threshold branch of the atomic swap contract. -/// 2 of 2 threshold of SpendPolicy::After and SpendPolicy::PublicKey -struct TimeThreshold; +#[cfg(test)] +mod test { + use super::*; + use crate::types::{Hash256, PublicKey}; -impl AtomicSwapComponent { - pub fn new(policy: SpendPolicy) -> Result { - if Self::is_valid_time_threshold(&policy) { - Ok(Self { - policy, - _marker: PhantomData, - }) - } else { - Err(AtomicSwapError::InvalidTimeThreshold(policy)) + fn pubkey0() -> PublicKey { + PublicKey::from_bytes( + &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + ).unwrap() + } + + fn pubkey1() -> PublicKey { + PublicKey::from_bytes( + &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), + ) + .unwrap() + } + + fn valid_atomic_swap_spend_policy() -> SpendPolicy { + SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(Hash256::default()), + SpendPolicy::PublicKey(pubkey0()), + ], + }, + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(0), + SpendPolicy::PublicKey(pubkey1()), + ], + }, + ], + } + } + + fn valid_component_hash_lock() -> SpendPolicy { + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(Hash256::default()), + SpendPolicy::PublicKey(pubkey0()), + ], } } - fn is_valid_time_threshold(policy: &SpendPolicy) -> bool { - match policy { - SpendPolicy::Threshold{ n: 2, of: [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] } => true, - _ => false, + fn valid_component_time_lock() -> SpendPolicy { + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(0), + SpendPolicy::PublicKey(pubkey1()), + ], } } -} + #[test] + fn test_atomic_swap_contract_valid() { + AtomicSwap::::new(valid_atomic_swap_spend_policy()).unwrap(); + } + #[test] + fn test_atomic_swap_contract_invalid_hash_lock_path() { + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![SpendPolicy::PublicKey(pubkey0()), valid_component_time_lock()], + }; + match AtomicSwap::::new(policy.clone()).unwrap_err() { + AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidSpendPolicyVariant(_)) => (), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_time_lock_path() { + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![valid_component_hash_lock(), SpendPolicy::PublicKey(pubkey0())], + }; + + match AtomicSwap::::new(policy.clone()).unwrap_err() { + AtomicSwapError::InvalidTimeComponent(ComponentError::TimeLockInvalidSpendPolicyVariant(_)) => (), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_components_wrong_order() { + let policy = SpendPolicy::Threshold { + n: 1, + of: vec![valid_component_time_lock(), valid_component_hash_lock()], + }; + + match AtomicSwap::::new(policy.clone()).unwrap_err() { + AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidThresholdStructure(_)) => (), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_components_too_many() { + let mut policy = valid_atomic_swap_spend_policy(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + of.push(SpendPolicy::PublicKey(pubkey0())); + }, + _ => unreachable!(), + } + + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidM { policy: _, m }) => { + assert_eq!(m, 3); + } + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_components_missing_time_lock_path() { + let mut policy = valid_atomic_swap_spend_policy(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + let _ = of.pop().unwrap(); + }, + _ => unreachable!(), + } + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidM { policy: _, m }) => { + assert_eq!(m, 1); + } + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_components_missing_hash_lock_path() { + let mut policy = valid_atomic_swap_spend_policy(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + let _ = of.remove(0); + }, + _ => unreachable!(), + } + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidM { policy: _, m }) => { + assert_eq!(m, 1); + } + _ => panic!(), + } + } + #[test] + fn test_atomic_swap_contract_invalid_components_missing_both_paths() { + let mut policy = valid_atomic_swap_spend_policy(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![]; + }, + _ => unreachable!(), + } + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidM { policy: _, m }) => { + assert_eq!(m, 0); + } + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_n() { + let mut policy = valid_atomic_swap_spend_policy(); + match &mut policy { + SpendPolicy::Threshold { n, .. } => *n = 10, + _ => unreachable!(), + } + + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidN { policy: _, n }) => { + assert_eq!(n, 10); + } + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_contract_invalid_policy_variant() { + let policy = SpendPolicy::PublicKey(pubkey0()); + + match AtomicSwap::::new(policy.clone()) { + Err(AtomicSwapError::InvalidSpendPolicyVariant { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_valid() { + AtomicSwapComponent::::new(valid_component_hash_lock()).unwrap(); + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_threshold_structure() { + let policy = SpendPolicy::Threshold { + n: 2, + of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], + }; + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::HashLockInvalidThresholdStructure(_) => (), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_valid() { + AtomicSwapComponent::::new(valid_component_time_lock()).unwrap(); + } +} From 4ce9455e734f67ec7b99504a4896610afe5b0e13 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 07:02:57 -0400 Subject: [PATCH 085/233] atomic swap component unit test --- src/types/atomic_swap.rs | 227 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 1 deletion(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 4566abc..1bdba34 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -441,7 +441,113 @@ mod test { }; match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { - ComponentError::HashLockInvalidThresholdStructure(_) => (), + ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_wrong_order() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + of.reverse(); + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_too_many() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + of.push(SpendPolicy::PublicKey(pubkey0())); + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 3), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_missing_public_key() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![SpendPolicy::Hash(Hash256::default())] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_missing_hash() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![SpendPolicy::PublicKey(pubkey0())] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_empty_threshold() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 0), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_n() { + let mut policy = valid_component_hash_lock(); + match &mut policy { + SpendPolicy::Threshold { n, of:_ } => { + *n = 10; + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::HashLockInvalidN{ policy: _, n } => assert_eq!(n, 10), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_hash_lock_invalid_policy_variant() { + let policy = SpendPolicy::PublicKey(pubkey0()); + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::HashLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), _ => panic!(), } } @@ -450,4 +556,123 @@ mod test { fn test_atomic_swap_component_time_lock_valid() { AtomicSwapComponent::::new(valid_component_time_lock()).unwrap(); } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_threshold_structure() { + let policy = SpendPolicy::Threshold { + n: 2, + of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], + }; + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_wrong_order() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + of.reverse(); + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_too_many() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + of.push(SpendPolicy::PublicKey(pubkey0())); + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 3), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_missing_public_key() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![SpendPolicy::After(0)] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_missing_time() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![SpendPolicy::PublicKey(pubkey1())] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_empty_threshold() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n:_, of } => { + *of = vec![] + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 0), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_n() { + let mut policy = valid_component_time_lock(); + match &mut policy { + SpendPolicy::Threshold { n, of:_ } => { + *n = 10; + }, + _ => unreachable!(), + } + + match AtomicSwapComponent::::new(policy).unwrap_err() { + ComponentError::TimeLockInvalidN{ policy: _, n } => assert_eq!(n, 10), + _ => panic!(), + } + } + + #[test] + fn test_atomic_swap_component_time_lock_invalid_policy_variant() { + let policy = SpendPolicy::PublicKey(pubkey0()); + + match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + ComponentError::TimeLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), + _ => panic!(), + } + } } From 6c039de792c3bc0203bbd3821c466ff9ef65555e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 07:03:16 -0400 Subject: [PATCH 086/233] edit atomic swap comments --- src/types/atomic_swap.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 1bdba34..36c16de 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -39,7 +39,9 @@ The resulting UTXO can then be spent by either Alice or Bob by meeting the condi It is only neccesary to reveal the path that will be satisfied. The other path will be opacified(see SpendPolicy::opacify) and replaced with SpendPolicy::Opaque(). -Alice can spend the UTXO by providing a signature, the secret and revealing the relevant path within the full SpendPolicy. Alice can construct the following SatisfiedPolicy to spend the UTXO: +Alice can spend the UTXO by providing a signature, the secret and revealing the relevant path within the full SpendPolicy. + +Alice can construct the following SatisfiedPolicy to spend the UTXO: SatisfiedPolicy { policy: SpendPolicy::Threshold { @@ -97,6 +99,7 @@ pub enum AtomicSwapError { /// Represents an atomic swap contract. /// PhantomData is used to enforce type safety on the structure of a SpendPolicy which is a complex recursive structure. +/// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. #[derive(Debug)] pub struct AtomicSwap { pub policy: SpendPolicy, From fbd234b424c3f7f8e4aecd1ea034e89ebc78f991 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 07:04:16 -0400 Subject: [PATCH 087/233] remove pub export of policy from AtomicSwap and AtomicSwapComponent wrappers; may revert this at some point --- src/types/atomic_swap.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 36c16de..5100319 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -102,7 +102,7 @@ pub enum AtomicSwapError { /// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. #[derive(Debug)] pub struct AtomicSwap { - pub policy: SpendPolicy, + policy: SpendPolicy, _marker: PhantomData, } @@ -121,6 +121,10 @@ impl AtomicSwap { }) } + pub fn policy(&self) -> &SpendPolicy { + &self.policy + } + fn is_valid(policy: &SpendPolicy) -> Result<(), AtomicSwapError> { match policy { SpendPolicy::Threshold { @@ -142,7 +146,7 @@ impl AtomicSwap { /// PhantomData is used to enforce type safety on the structure of the SpendPolicy. #[derive(Debug)] pub struct AtomicSwapComponent { - pub policy: SpendPolicy, + policy: SpendPolicy, _marker: PhantomData, } @@ -186,6 +190,10 @@ impl AtomicSwapComponent { }) } + pub fn policy(&self) -> &SpendPolicy { + &self.policy + } + fn is_valid(policy: &SpendPolicy) -> Result<(), ComponentError> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { From fb2d38706a29e9c59f8afd126da6a8b9413fc4fe Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 08:05:44 -0400 Subject: [PATCH 088/233] remove unnecessary generalization --- src/types/atomic_swap.rs | 76 +++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 5100319..43f9532 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -1,4 +1,4 @@ -use crate::types::SpendPolicy; +use crate::types::{Address, SatisfiedPolicy, SpendPolicy}; use std::marker::PhantomData; use thiserror::Error; @@ -98,31 +98,27 @@ pub enum AtomicSwapError { } /// Represents an atomic swap contract. -/// PhantomData is used to enforce type safety on the structure of a SpendPolicy which is a complex recursive structure. -/// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. -#[derive(Debug)] -pub struct AtomicSwap { - policy: SpendPolicy, - _marker: PhantomData, -} - -/// The full atomic swap contract. /// SpendPolicy:address is used to generate the address of the contract. /// Funds can then be locked in the contract by creating a transaction output with this address. -/// This is used only to create outputs, never inputs. +/// This is used only to create outputs, never inputs./// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. #[derive(Debug)] -struct Contract; +pub struct AtomicSwap(SpendPolicy); -impl AtomicSwap { +impl AtomicSwap { pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self { - policy, - _marker: PhantomData, - }) + Self::is_valid(&policy).map(|_| Self(policy)) } pub fn policy(&self) -> &SpendPolicy { - &self.policy + &self.0 + } + + pub fn address(&self) -> Address { + self.policy().address() + } + + pub fn opacify(&self) -> SpendPolicy { + self.policy().opacify() } fn is_valid(policy: &SpendPolicy) -> Result<(), AtomicSwapError> { @@ -240,6 +236,30 @@ impl AtomicSwapComponent { } } +enum SatisfiedAtomicSwapError { + InvalidSomething +} + +/// Represnts a satisfied atomic swap contract. +#[derive(Debug)] +pub struct SatisfiedAtomicSwap { + satisfied_policy: SatisfiedPolicy, + _marker: PhantomData, +} + +impl SatisfiedAtomicSwap { + pub fn new(satisfied_policy: SatisfiedPolicy) -> Result { + Self::is_valid(&satisfied_policy).map(|_| Self { + satisfied_policy, + _marker: PhantomData, + }) + } + + fn is_valid(satisfied_policy: &SatisfiedPolicy) -> Result<(), SatisfiedAtomicSwapError> { + todo!() + } +} + #[cfg(test)] mod test { use super::*; @@ -302,7 +322,7 @@ mod test { #[test] fn test_atomic_swap_contract_valid() { - AtomicSwap::::new(valid_atomic_swap_spend_policy()).unwrap(); + AtomicSwap::new(valid_atomic_swap_spend_policy()).unwrap(); } #[test] @@ -312,7 +332,7 @@ mod test { of: vec![SpendPolicy::PublicKey(pubkey0()), valid_component_time_lock()], }; - match AtomicSwap::::new(policy.clone()).unwrap_err() { + match AtomicSwap::new(policy.clone()).unwrap_err() { AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidSpendPolicyVariant(_)) => (), _ => panic!(), } @@ -325,7 +345,7 @@ mod test { of: vec![valid_component_hash_lock(), SpendPolicy::PublicKey(pubkey0())], }; - match AtomicSwap::::new(policy.clone()).unwrap_err() { + match AtomicSwap::new(policy.clone()).unwrap_err() { AtomicSwapError::InvalidTimeComponent(ComponentError::TimeLockInvalidSpendPolicyVariant(_)) => (), _ => panic!(), } @@ -338,7 +358,7 @@ mod test { of: vec![valid_component_time_lock(), valid_component_hash_lock()], }; - match AtomicSwap::::new(policy.clone()).unwrap_err() { + match AtomicSwap::new(policy.clone()).unwrap_err() { AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidThresholdStructure(_)) => (), _ => panic!(), } @@ -354,7 +374,7 @@ mod test { _ => unreachable!(), } - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 3); } @@ -371,7 +391,7 @@ mod test { }, _ => unreachable!(), } - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 1); } @@ -388,7 +408,7 @@ mod test { }, _ => unreachable!(), } - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 1); } @@ -405,7 +425,7 @@ mod test { }, _ => unreachable!(), } - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 0); } @@ -421,7 +441,7 @@ mod test { _ => unreachable!(), } - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidN { policy: _, n }) => { assert_eq!(n, 10); } @@ -433,7 +453,7 @@ mod test { fn test_atomic_swap_contract_invalid_policy_variant() { let policy = SpendPolicy::PublicKey(pubkey0()); - match AtomicSwap::::new(policy.clone()) { + match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidSpendPolicyVariant { .. }) => (), _ => panic!(), } From 296bbbad166ee4fb7bd6ad25e2a951dd5490cb1b Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 09:23:45 -0400 Subject: [PATCH 089/233] remove PhantomData pattern --- src/types/atomic_swap.rs | 92 ++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 43f9532..8a52583 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -1,6 +1,4 @@ use crate::types::{Address, SatisfiedPolicy, SpendPolicy}; - -use std::marker::PhantomData; use thiserror::Error; /* @@ -127,8 +125,8 @@ impl AtomicSwap { n: 1, of } if of.len() == 2 => { - AtomicSwapComponent::::is_valid(&of[0]).map_err(AtomicSwapError::InvalidHashComponent)?; - AtomicSwapComponent::::is_valid(&of[1]).map_err(AtomicSwapError::InvalidTimeComponent)?; + HashLockPath::is_valid(&of[0]).map_err(AtomicSwapError::InvalidHashComponent)?; + TimeLockPath::is_valid(&of[1]).map_err(AtomicSwapError::InvalidTimeComponent)?; Ok(()) }, SpendPolicy::Threshold { n: 1, of } => Err(AtomicSwapError::InvalidM{ policy: policy.clone(), m: of.len() }), @@ -138,14 +136,6 @@ impl AtomicSwap { } } -/// Represents a fulfillment path of the atomic swap contract. ie, HashLock or TimeLock. -/// PhantomData is used to enforce type safety on the structure of the SpendPolicy. -#[derive(Debug)] -pub struct AtomicSwapComponent { - policy: SpendPolicy, - _marker: PhantomData, -} - #[derive(Debug, Error)] pub enum ComponentError { #[error("invalid hash lock component, hash lock path: {:?}", .0)] @@ -166,6 +156,12 @@ pub enum ComponentError { TimeLockInvalidM{ policy: SpendPolicy, m : usize }, } +trait SpendPolicyValidator { + type Error; + + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error>; +} + /// The hash locked threshold path of the atomic swap contract. /// 2 of 2 threshold of: /// SpendPolicy::Hash() and SpendPolicy::PublicKey() @@ -176,21 +172,22 @@ pub enum ComponentError { /// - sha256(secret) == hash /// - length(secret) == 32 #[derive(Debug)] -struct HashLockPath; +pub struct HashLockPath(SpendPolicy); -impl AtomicSwapComponent { +impl HashLockPath { pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self { - policy, - _marker: PhantomData, - }) + Self::is_valid(&policy).map(|_| Self(policy)) } pub fn policy(&self) -> &SpendPolicy { - &self.policy + &self.0 } +} + +impl SpendPolicyValidator for HashLockPath { + type Error = ComponentError; - fn is_valid(policy: &SpendPolicy) -> Result<(), ComponentError> { + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { match of.as_slice() { @@ -211,17 +208,22 @@ impl AtomicSwapComponent { /// - signature from participant's public key /// - timestamp has passed #[derive(Debug)] -struct TimeLockPath; +pub struct TimeLockPath(SpendPolicy); -impl AtomicSwapComponent { +impl TimeLockPath { pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self { - policy, - _marker: PhantomData, - }) + Self::is_valid(&policy).map(|_| Self(policy)) } - fn is_valid(policy: &SpendPolicy) -> Result<(), ComponentError> { + pub fn policy(&self) -> &SpendPolicy { + &self.0 + } +} + +impl SpendPolicyValidator for TimeLockPath { + type Error = ComponentError; + + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { match of.as_slice() { @@ -461,7 +463,7 @@ mod test { #[test] fn test_atomic_swap_component_hash_lock_valid() { - AtomicSwapComponent::::new(valid_component_hash_lock()).unwrap(); + HashLockPath::new(valid_component_hash_lock()).unwrap(); } #[test] @@ -471,7 +473,7 @@ mod test { of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], }; - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match HashLockPath::new(policy.clone()).unwrap_err() { ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), _ => panic!(), } @@ -487,7 +489,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match HashLockPath::new(policy.clone()).unwrap_err() { ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), _ => panic!(), } @@ -503,7 +505,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match HashLockPath::new(policy).unwrap_err() { ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 3), _ => panic!(), } @@ -519,7 +521,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match HashLockPath::new(policy).unwrap_err() { ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), _ => panic!(), } @@ -535,7 +537,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match HashLockPath::new(policy).unwrap_err() { ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), _ => panic!(), } @@ -551,7 +553,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match HashLockPath::new(policy).unwrap_err() { ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 0), _ => panic!(), } @@ -567,7 +569,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match HashLockPath::new(policy).unwrap_err() { ComponentError::HashLockInvalidN{ policy: _, n } => assert_eq!(n, 10), _ => panic!(), } @@ -577,7 +579,7 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_policy_variant() { let policy = SpendPolicy::PublicKey(pubkey0()); - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match HashLockPath::new(policy.clone()).unwrap_err() { ComponentError::HashLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), _ => panic!(), } @@ -585,7 +587,7 @@ mod test { #[test] fn test_atomic_swap_component_time_lock_valid() { - AtomicSwapComponent::::new(valid_component_time_lock()).unwrap(); + TimeLockPath::new(valid_component_time_lock()).unwrap(); } #[test] @@ -595,7 +597,7 @@ mod test { of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], }; - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match TimeLockPath::new(policy.clone()).unwrap_err() { ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), _ => panic!(), } @@ -611,7 +613,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match TimeLockPath::new(policy.clone()).unwrap_err() { ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), _ => panic!(), } @@ -627,7 +629,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match TimeLockPath::new(policy).unwrap_err() { ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 3), _ => panic!(), } @@ -643,7 +645,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match TimeLockPath::new(policy).unwrap_err() { ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), _ => panic!(), } @@ -659,7 +661,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match TimeLockPath::new(policy).unwrap_err() { ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), _ => panic!(), } @@ -675,7 +677,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match TimeLockPath::new(policy).unwrap_err() { ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 0), _ => panic!(), } @@ -691,7 +693,7 @@ mod test { _ => unreachable!(), } - match AtomicSwapComponent::::new(policy).unwrap_err() { + match TimeLockPath::new(policy).unwrap_err() { ComponentError::TimeLockInvalidN{ policy: _, n } => assert_eq!(n, 10), _ => panic!(), } @@ -701,7 +703,7 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_policy_variant() { let policy = SpendPolicy::PublicKey(pubkey0()); - match AtomicSwapComponent::::new(policy.clone()).unwrap_err() { + match TimeLockPath::new(policy.clone()).unwrap_err() { ComponentError::TimeLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), _ => panic!(), } From 979997d9e47ba9b51c1f54090d4c4f133a7bb5d1 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 09:37:42 -0400 Subject: [PATCH 090/233] introduce IsValidatedSpendPolicy trait --- src/types/atomic_swap.rs | 74 +++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 8a52583..134dc40 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -81,6 +81,18 @@ SatisfiedPolicy { */ +/// Represents a validated SpendPolicy. Each unique structure of SpendPolicy should implement this trait. +/// The stored SpendPolicy must not be exposed via pub +/// otherwise a consumer can initialize with an invalid SpendPolicy. ie, AtomicSwap(invalid_policy) +trait IsValidatedSpendPolicy { + type Error; + + // allow reference to inner policy because it is not public + fn policy(&self) -> &SpendPolicy; + + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error>; +} + #[derive(Debug, Error)] pub enum AtomicSwapError { #[error("invalid atomic swap, invalid hash component: {}", .0)] @@ -107,10 +119,6 @@ impl AtomicSwap { Self::is_valid(&policy).map(|_| Self(policy)) } - pub fn policy(&self) -> &SpendPolicy { - &self.0 - } - pub fn address(&self) -> Address { self.policy().address() } @@ -118,8 +126,16 @@ impl AtomicSwap { pub fn opacify(&self) -> SpendPolicy { self.policy().opacify() } +} + +impl IsValidatedSpendPolicy for AtomicSwap { + type Error = AtomicSwapError; + + fn policy(&self) -> &SpendPolicy { + &self.0 + } - fn is_valid(policy: &SpendPolicy) -> Result<(), AtomicSwapError> { + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold { n: 1, @@ -156,12 +172,6 @@ pub enum ComponentError { TimeLockInvalidM{ policy: SpendPolicy, m : usize }, } -trait SpendPolicyValidator { - type Error; - - fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error>; -} - /// The hash locked threshold path of the atomic swap contract. /// 2 of 2 threshold of: /// SpendPolicy::Hash() and SpendPolicy::PublicKey() @@ -179,14 +189,15 @@ impl HashLockPath { Self::is_valid(&policy).map(|_| Self(policy)) } - pub fn policy(&self) -> &SpendPolicy { - &self.0 - } } -impl SpendPolicyValidator for HashLockPath { +impl IsValidatedSpendPolicy for HashLockPath { type Error = ComponentError; + fn policy(&self) -> &SpendPolicy { + &self.0 + } + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { @@ -214,15 +225,15 @@ impl TimeLockPath { pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } - - pub fn policy(&self) -> &SpendPolicy { - &self.0 - } } -impl SpendPolicyValidator for TimeLockPath { +impl IsValidatedSpendPolicy for TimeLockPath { type Error = ComponentError; + fn policy(&self) -> &SpendPolicy { + &self.0 + } + fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { @@ -238,29 +249,6 @@ impl SpendPolicyValidator for TimeLockPath { } } -enum SatisfiedAtomicSwapError { - InvalidSomething -} - -/// Represnts a satisfied atomic swap contract. -#[derive(Debug)] -pub struct SatisfiedAtomicSwap { - satisfied_policy: SatisfiedPolicy, - _marker: PhantomData, -} - -impl SatisfiedAtomicSwap { - pub fn new(satisfied_policy: SatisfiedPolicy) -> Result { - Self::is_valid(&satisfied_policy).map(|_| Self { - satisfied_policy, - _marker: PhantomData, - }) - } - - fn is_valid(satisfied_policy: &SatisfiedPolicy) -> Result<(), SatisfiedAtomicSwapError> { - todo!() - } -} #[cfg(test)] mod test { From b7c8e412753f43412a7c8dc8d5e7b8b0e309b0fd Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 09:41:10 -0400 Subject: [PATCH 091/233] fix doc comment --- src/types/atomic_swap.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 134dc40..5e63529 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -110,7 +110,8 @@ pub enum AtomicSwapError { /// Represents an atomic swap contract. /// SpendPolicy:address is used to generate the address of the contract. /// Funds can then be locked in the contract by creating a transaction output with this address. -/// This is used only to create outputs, never inputs./// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. +/// This is used only to create outputs, never inputs. +/// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. #[derive(Debug)] pub struct AtomicSwap(SpendPolicy); From 6d80c1669bfe50ac02135866c2ea282d5ea99fe7 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 26 Sep 2024 10:29:38 -0400 Subject: [PATCH 092/233] generalize IsValidatedPolicy to support SatisfiedPolicy --- src/types/atomic_swap.rs | 102 ++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 23 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 5e63529..3602c44 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -81,16 +81,19 @@ SatisfiedPolicy { */ -/// Represents a validated SpendPolicy. Each unique structure of SpendPolicy should implement this trait. -/// The stored SpendPolicy must not be exposed via pub -/// otherwise a consumer can initialize with an invalid SpendPolicy. ie, AtomicSwap(invalid_policy) -trait IsValidatedSpendPolicy { +/// SpendPolicy is a recursive structure that represents the conditions required to spend a UTXO. +/// This trait enables having wrapper types that enforce a specific structure. +/// Each specific structure of SpendPolicy or SatisfiedPolicy should implement this trait. +/// The stored inner type must not be exposed via pub +/// otherwise a consumer can initialize with invalid data. ie, AtomicSwap(invalid_policy) +trait IsValidatedPolicy { type Error; + type Inner; // allow reference to inner policy because it is not public - fn policy(&self) -> &SpendPolicy; + fn inner(&self) -> &Self::Inner; - fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error>; + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error>; } #[derive(Debug, Error)] @@ -107,7 +110,7 @@ pub enum AtomicSwapError { InvalidSpendPolicyVariant(SpendPolicy), } -/// Represents an atomic swap contract. +/// Represents an atomic swap contract. Structure is documented above. /// SpendPolicy:address is used to generate the address of the contract. /// Funds can then be locked in the contract by creating a transaction output with this address. /// This is used only to create outputs, never inputs. @@ -121,22 +124,23 @@ impl AtomicSwap { } pub fn address(&self) -> Address { - self.policy().address() + self.inner().address() } pub fn opacify(&self) -> SpendPolicy { - self.policy().opacify() + self.inner().opacify() } } -impl IsValidatedSpendPolicy for AtomicSwap { +impl IsValidatedPolicy for AtomicSwap { type Error = AtomicSwapError; + type Inner = SpendPolicy; - fn policy(&self) -> &SpendPolicy { + fn inner(&self) -> &Self::Inner { &self.0 } - fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold { n: 1, @@ -174,10 +178,14 @@ pub enum ComponentError { } /// The hash locked threshold path of the atomic swap contract. -/// 2 of 2 threshold of: -/// SpendPolicy::Hash() and SpendPolicy::PublicKey() -/// where: -/// secret_hash == sha256(secret) +/// structure: +/// SpendPolicy::Threshold { +/// n: 2, +/// of: vec![ +/// SpendPolicy::Hash(sha256(secret)), +/// SpendPolicy::PublicKey(public_key) +/// ] +/// } /// fulfillment conditions: /// - signature from participant's public key /// - sha256(secret) == hash @@ -192,14 +200,15 @@ impl HashLockPath { } -impl IsValidatedSpendPolicy for HashLockPath { +impl IsValidatedPolicy for HashLockPath { type Error = ComponentError; + type Inner = SpendPolicy; - fn policy(&self) -> &SpendPolicy { + fn inner(&self) -> &Self::Inner { &self.0 } - fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { match of.as_slice() { @@ -215,7 +224,14 @@ impl IsValidatedSpendPolicy for HashLockPath { } /// The time based threshold path of the atomic swap contract. -/// 2 of 2 threshold of SpendPolicy::After(timestamp) and SpendPolicy::PublicKey(participant's public key) +/// structure: +/// SpendPolicy::Threshold { +/// n: 2, +/// of: vec![ +/// SpendPolicy::After(timestamp), +/// SpendPolicy::PublicKey(public_key) +/// ] +/// } /// fulfillment conditions: /// - signature from participant's public key /// - timestamp has passed @@ -228,14 +244,15 @@ impl TimeLockPath { } } -impl IsValidatedSpendPolicy for TimeLockPath { +impl IsValidatedPolicy for TimeLockPath { type Error = ComponentError; + type Inner = SpendPolicy; - fn policy(&self) -> &SpendPolicy { + fn inner(&self) -> &Self::Inner { &self.0 } - fn is_valid(policy: &SpendPolicy) -> Result<(), Self::Error> { + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { match of.as_slice() { @@ -250,6 +267,45 @@ impl IsValidatedSpendPolicy for TimeLockPath { } } +enum SatisfiedAtomicSwapError { + InvalidSomething +} + +/// Represents the SatisfiedPolicy for the success path of an atomic swap. +/// Used within the input that spends the locked UTXO. +/// structure: +/// SatisfiedPolicy { +/// policy: SpendPolicy::Threshold { +/// n: 1, +/// of: vec![ +/// SpendPolicy::Threshold { +/// n: 2, +/// of: vec![ +/// SpendPolicy::Hash(sha256(secret)), +/// SpendPolicy::PublicKey(public_key) +/// ] +/// }, +/// SpendPolicy::Opaque(), +/// ] +/// }, +/// signatures: vec![signature], +/// preimages: vec![secret] +/// } +#[derive(Debug, Clone, PartialEq)] +pub struct AtomicSwapSuccess(SatisfiedPolicy); + +impl IsValidatedPolicy for AtomicSwapSuccess { + type Error = SatisfiedAtomicSwapError; + type Inner = SatisfiedPolicy; + + fn inner(&self) -> &Self::Inner { + &self.0 + } + + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { + todo!() + } +} #[cfg(test)] mod test { From 2b8676144873bb051446f7d14f2ccd02f6755feb Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 1 Oct 2024 06:36:39 -0400 Subject: [PATCH 093/233] add opaque SpendPolicy path --- src/types/atomic_swap.rs | 62 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 3602c44..cb3fdf8 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -175,6 +175,8 @@ pub enum ComponentError { TimeLockInvalidN{ policy: SpendPolicy, n : u8 }, #[error("invalid time lock component, wrong m:{} policy: {:?}", m, policy)] TimeLockInvalidM{ policy: SpendPolicy, m : usize }, + #[error("invalid component, not SpendPolicy::Opaque: {:?}", .0)] + OpaqueInvalidSpendPolicyVariant(SpendPolicy), } /// The hash locked threshold path of the atomic swap contract. @@ -267,8 +269,29 @@ impl IsValidatedPolicy for TimeLockPath { } } -enum SatisfiedAtomicSwapError { - InvalidSomething +#[derive(Debug)] +pub struct Opaque(SpendPolicy); + +impl Opaque { + pub fn new(policy: SpendPolicy) -> Result { + Self::is_valid(&policy).map(|_| Self(policy)) + } +} + +impl IsValidatedPolicy for Opaque { + type Error = ComponentError; + type Inner = SpendPolicy; + + fn inner(&self) -> &Self::Inner { + &self.0 + } + + fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { + match policy { + SpendPolicy::Opaque(_) => Ok(()), + _ => Err(ComponentError::OpaqueInvalidSpendPolicyVariant(policy.clone())), + } + } } /// Represents the SatisfiedPolicy for the success path of an atomic swap. @@ -294,16 +317,45 @@ enum SatisfiedAtomicSwapError { #[derive(Debug, Clone, PartialEq)] pub struct AtomicSwapSuccess(SatisfiedPolicy); +#[derive(Debug, Error)] +enum AtomicSwapSuccessError { + InvalidSignaturesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, + InvalidPreimagesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, + InvalidSpendPolicyVariant { satisfied_policy: SatisfiedPolicy }, + AtomicSwapError(AtomicSwapError), +} + impl IsValidatedPolicy for AtomicSwapSuccess { - type Error = SatisfiedAtomicSwapError; + type Error = AtomicSwapSuccessError; type Inner = SatisfiedPolicy; fn inner(&self) -> &Self::Inner { &self.0 } - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { - todo!() + fn is_valid(satisfied_policy: &Self::Inner) -> Result<(), Self::Error> { + if satisfied_policy.signatures.len() != 1 { + return Err(AtomicSwapSuccessError::InvalidSignaturesAmount { + satisfied_policy: satisfied_policy.clone(), + amount: satisfied_policy.signatures.len(), + }); + } + + if satisfied_policy.preimages.len() != 1 { + return Err(AtomicSwapSuccessError::InvalidPreimagesAmount { + satisfied_policy: satisfied_policy.clone(), + amount: satisfied_policy.preimages.len(), + }); + } + + match satisfied_policy.policy { + SpendPolicy::Threshold{ n: 1, of } if of.len() == 2 => { + HashLockPath::is_valid(&of[0]).map_err(AtomicSwapSuccessError::AtomicSwapError)?; + Opaque::is_valid(&of[1]).map_err(AtomicSwapSuccessError::AtomicSwapError)?; + Ok(()) + }, + _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant(satisfied_policy.clone())), + } } } From 0376b292e63752db84c784246af3333aabb47c14 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 2 Oct 2024 14:01:47 -0400 Subject: [PATCH 094/233] fix misnomer; change "http"->"transport" --- src/lib.rs | 2 +- src/{http => transport}/client.rs | 2 +- src/{http => transport}/client/example.rs | 4 ++-- src/{http => transport}/client/native.rs | 6 +++--- src/{http => transport}/client/wasm.rs | 6 +++--- src/{http => transport}/client/wasm/wasm_fetch.rs | 0 src/{http => transport}/endpoints.rs | 2 +- src/{http => transport}/mod.rs | 0 8 files changed, 11 insertions(+), 11 deletions(-) rename src/{http => transport}/client.rs (98%) rename src/{http => transport}/client/example.rs (95%) rename src/{http => transport}/client/native.rs (95%) rename src/{http => transport}/client/wasm.rs (96%) rename src/{http => transport}/client/wasm/wasm_fetch.rs (100%) rename src/{http => transport}/endpoints.rs (99%) rename src/{http => transport}/mod.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index fecce53..9878c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ mod macros; pub mod blake2b_internal; pub mod encoding; -pub mod http; +pub mod transport; pub mod types; #[cfg(test)] mod tests; diff --git a/src/http/client.rs b/src/transport/client.rs similarity index 98% rename from src/http/client.rs rename to src/transport/client.rs index f7da538..ab48f06 100644 --- a/src/http/client.rs +++ b/src/transport/client.rs @@ -1,4 +1,4 @@ -use crate::http::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, SiaApiRequest}; +use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, SiaApiRequest}; use crate::types::Address; use async_trait::async_trait; diff --git a/src/http/client/example.rs b/src/transport/client/example.rs similarity index 95% rename from src/http/client/example.rs rename to src/transport/client/example.rs index 6b7e9da..19dcec4 100644 --- a/src/http/client/example.rs +++ b/src/transport/client/example.rs @@ -1,10 +1,10 @@ // THIS FILE MUST NOT INCLUDED IN WORKSPACE **Some Broken Code to Ensure the Build Fails if this File is Included in the Workspace** -use crate::http::endpoints::{SiaApiRequest}; +use crate::transport::endpoints::{SiaApiRequest}; use async_trait::async_trait; use serde::Deserialize; -use crate::http::client::{ApiClient, ApiClientError, ApiClientHelpers, EndpointSchema}; +use crate::transport::client::{ApiClient, ApiClientError, ApiClientHelpers, EndpointSchema}; #[derive(Clone)] diff --git a/src/http/client/native.rs b/src/transport/client/native.rs similarity index 95% rename from src/http/client/native.rs rename to src/transport/client/native.rs index 8e5cf33..f894568 100644 --- a/src/http/client/native.rs +++ b/src/transport/client/native.rs @@ -1,4 +1,4 @@ -use crate::http::endpoints::{ConsensusTipRequest, SiaApiRequest}; +use crate::transport::endpoints::{ConsensusTipRequest, SiaApiRequest}; use async_trait::async_trait; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; @@ -7,7 +7,7 @@ use reqwest::Client as ReqwestClient; use serde::Deserialize; use url::Url; -use crate::http::client::{ApiClient, ApiClientError, ApiClientHelpers, Body as ClientBody, EndpointSchema}; +use crate::transport::client::{ApiClient, ApiClientError, ApiClientHelpers, Body as ClientBody, EndpointSchema}; use core::time::Duration; #[derive(Clone)] @@ -119,7 +119,7 @@ impl ApiClientHelpers for NativeClient {} #[cfg(test)] mod tests { use super::*; - use crate::http::endpoints::{AddressBalanceRequest, GetEventRequest}; + use crate::transport::endpoints::{AddressBalanceRequest, GetEventRequest}; use crate::types::Address; use std::str::FromStr; diff --git a/src/http/client/wasm.rs b/src/transport/client/wasm.rs similarity index 96% rename from src/http/client/wasm.rs rename to src/transport/client/wasm.rs index fec5616..9ecfea2 100644 --- a/src/http/client/wasm.rs +++ b/src/transport/client/wasm.rs @@ -1,5 +1,5 @@ -use crate::http::client::{ApiClient, ApiClientError, ApiClientHelpers, Body, EndpointSchema, SchemaMethod}; -use crate::http::endpoints::{ConsensusTipRequest, SiaApiRequest}; +use crate::transport::client::{ApiClient, ApiClientError, ApiClientHelpers, Body, EndpointSchema, SchemaMethod}; +use crate::transport::endpoints::{ConsensusTipRequest, SiaApiRequest}; use async_trait::async_trait; use http::StatusCode; @@ -130,7 +130,7 @@ mod wasm_tests { #[wasm_bindgen_test] async fn test_sia_wasm_client_client_error() { - use crate::http::endpoints::TxpoolBroadcastRequest; + use crate::transport::endpoints::TxpoolBroadcastRequest; use crate::types::V2Transaction; let client = Client::new(CONF.clone()).await.unwrap(); diff --git a/src/http/client/wasm/wasm_fetch.rs b/src/transport/client/wasm/wasm_fetch.rs similarity index 100% rename from src/http/client/wasm/wasm_fetch.rs rename to src/transport/client/wasm/wasm_fetch.rs diff --git a/src/http/endpoints.rs b/src/transport/endpoints.rs similarity index 99% rename from src/http/endpoints.rs rename to src/transport/endpoints.rs index f2d4a79..dfbb8ee 100644 --- a/src/http/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,4 +1,4 @@ -use crate::http::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; +use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; use crate::types::{Address, BlockID, Event, Hash256, Currency, SiacoinElement, V1Transaction, V2Transaction}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; diff --git a/src/http/mod.rs b/src/transport/mod.rs similarity index 100% rename from src/http/mod.rs rename to src/transport/mod.rs From 9b79ae3605d3deaa1a04076ae86530b8f84065c4 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 2 Oct 2024 14:02:37 -0400 Subject: [PATCH 095/233] fix atomic_swap compilation - this file is likely to be fully refactored to reduce complexity --- src/types/atomic_swap.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index cb3fdf8..06d0603 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -319,10 +319,16 @@ pub struct AtomicSwapSuccess(SatisfiedPolicy); #[derive(Debug, Error)] enum AtomicSwapSuccessError { + #[error("invalid atomic swap success: {:?}, invalid signatures amount: {}", satisfied_policy, amount)] InvalidSignaturesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, + #[error("invalid atomic swap success: {:?}, invalid preimages amount: {}", satisfied_policy, amount)] InvalidPreimagesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, + #[error("invalid atomic swap success: {:?}, invalid policy variant", satisfied_policy)] InvalidSpendPolicyVariant { satisfied_policy: SatisfiedPolicy }, - AtomicSwapError(AtomicSwapError), + // #[error("invalid atomic swap success: {0}, atomic swap error")] + // AtomicSwapError(AtomicSwapError), + #[error("invalid atomic swap success, TODO remove this placeholder")] + PlaceHolder, } impl IsValidatedPolicy for AtomicSwapSuccess { @@ -348,13 +354,13 @@ impl IsValidatedPolicy for AtomicSwapSuccess { }); } - match satisfied_policy.policy { + match &satisfied_policy.policy { SpendPolicy::Threshold{ n: 1, of } if of.len() == 2 => { - HashLockPath::is_valid(&of[0]).map_err(AtomicSwapSuccessError::AtomicSwapError)?; - Opaque::is_valid(&of[1]).map_err(AtomicSwapSuccessError::AtomicSwapError)?; + HashLockPath::is_valid(&of[0]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; + Opaque::is_valid(&of[1]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; Ok(()) }, - _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant(satisfied_policy.clone())), + _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant{ satisfied_policy: satisfied_policy.clone()}), } } } From b5db130d03d5bddc81bdabf7fcda5f021f3b9157 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 00:04:57 -0400 Subject: [PATCH 096/233] add address and v1_address method to PublicKey --- src/types/keypair.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 8fb6299..f501874 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use thiserror::Error; -use crate::types::Signature; +use crate::types::{Address, SpendPolicy, Signature}; #[derive(Debug, Error)] pub enum KeypairError { @@ -105,6 +105,16 @@ impl PublicKey { false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), } } + + /// Generate the default v1 address from the public key + pub fn v1_address(&self) -> Address { + SpendPolicy::unlock_condition(vec![self.clone()], 0, 1).address() + } + + /// Generate the default v2 address from the public key + pub fn address(&self) -> Address { + SpendPolicy::PublicKey(self.clone()).address() + } } impl<'de> Deserialize<'de> for PublicKey { From a0a7b94bebcfd7db596ee56bc9de51ef8929e92c Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 00:05:28 -0400 Subject: [PATCH 097/233] add unlock_condition helper method to SpendPolicy --- src/types/spend_policy.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 7650e28..9c55a66 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -126,6 +126,10 @@ impl SpendPolicy { pub fn opaque(p: &SpendPolicy) -> Self { SpendPolicy::Opaque(p.address()) } + pub fn unlock_condition(pubkeys: Vec, timelock: u64, signatures_required: u64) -> Self { + SpendPolicy::UnlockConditions(UnlockCondition::new(pubkeys, timelock, signatures_required)) + } + pub fn anyone_can_spend() -> Self { SpendPolicy::threshold(0, vec![]) } pub fn opacify(&self) -> Self { SpendPolicy::Opaque(self.address()) } From 04e3bee55679556e773f1dfef09aa7ed303f6c12 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 03:48:51 -0400 Subject: [PATCH 098/233] reintroduce Keypair::public method; add doc comment for Keypair --- src/types/keypair.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index f501874..217de24 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -21,8 +21,13 @@ pub enum KeypairError { PublicKeyParseBytes(Ed25519SignatureError), } +/// A Sia Public-Private Keypair +/// The purpose of this wrapper type is to limit the functionality of underlying ed25519 types. +/// The inner fields are not public by design. +/// We must not allow the consumer to create an invalid ed25519 Keypair or edit the PublicKey after creation. +/// see https://github.com/advisories/GHSA-w5vr-6qhr-36cc pub struct Keypair { - pub public: PublicKey, + public: PublicKey, private: PrivateKey, } @@ -54,6 +59,10 @@ impl Keypair { pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { Verifier::verify(self, message, signature) } + + pub fn public(&self) -> PublicKey { + self.public.clone() + } } struct PrivateKey(SecretKey); From 5d00f8c9cc75b72f0cd19a573e7700c974e817f7 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 03:49:27 -0400 Subject: [PATCH 099/233] use Keypair::public method instead of direct ref to struct member --- src/types/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 5372fde..04ed5b6 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1272,13 +1272,13 @@ impl V2TransactionBuilder { let sig = keypair.sign(&sig_hash.0); for si in &mut self.siacoin_inputs { match &si.satisfied_policy.policy { - SpendPolicy::PublicKey(pk) if pk == &keypair.public => { + SpendPolicy::PublicKey(pk) if pk == &keypair.public() => { si.satisfied_policy.signatures.push(sig.clone()) }, SpendPolicy::UnlockConditions(uc) => { for p in &uc.unlock_keys { match p { - UnlockKey::Ed25519(pk) if pk == &keypair.public => { + UnlockKey::Ed25519(pk) if pk == &keypair.public() => { si.satisfied_policy.signatures.push(sig.clone()) }, _ => (), From b8014cb6f391ae513b8bf86cac0517d567141756 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 03:51:00 -0400 Subject: [PATCH 100/233] add temp bandaid hack to allow KDF compilation; the impl is unused but required due to derive(Clone) on KDF's PrivKeyPolicy --- src/types/keypair.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 217de24..8222592 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -31,6 +31,13 @@ pub struct Keypair { private: PrivateKey, } +// FIXME Critical; this Clone must be removed once DeFi Framework issue#2241 is addressed +impl Clone for Keypair { + fn clone(&self) -> Self { + unimplemented!() + } +} + impl Signer for Keypair { /// Sign a message with this keypair's secret key. fn try_sign(&self, message: &[u8]) -> Result { From 437ed06aff15108de5b186fec66dd8837fbff11a Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 07:28:04 -0400 Subject: [PATCH 101/233] remove placeholder Clone impl --- src/types/keypair.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 8222592..217de24 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -31,13 +31,6 @@ pub struct Keypair { private: PrivateKey, } -// FIXME Critical; this Clone must be removed once DeFi Framework issue#2241 is addressed -impl Clone for Keypair { - fn clone(&self) -> Self { - unimplemented!() - } -} - impl Signer for Keypair { /// Sign a message with this keypair's secret key. fn try_sign(&self, message: &[u8]) -> Result { From 2ff24bc0917412abf995b83e68d9b839dca5c995 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 13 Oct 2024 07:28:32 -0400 Subject: [PATCH 102/233] add Keypair::private method --- src/types/keypair.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 217de24..33a9521 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -1,6 +1,6 @@ use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::{ExpandedSecretKey, PublicKey as Ed25519PublicKey, SecretKey, - SignatureError as Ed25519SignatureError, Signer, Verifier}; + SignatureError as Ed25519SignatureError, Signer, Verifier, SECRET_KEY_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use thiserror::Error; @@ -63,6 +63,10 @@ impl Keypair { pub fn public(&self) -> PublicKey { self.public.clone() } + + pub fn private(&self) -> [u8; SECRET_KEY_LENGTH] { + self.private.0.to_bytes() + } } struct PrivateKey(SecretKey); From fbbb1d7927b4d33d9652ba14bdb0076e79b81c7a Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 14 Oct 2024 13:07:22 -0400 Subject: [PATCH 103/233] use native rust ssl for all targets --- Cargo.toml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1458cf..3188bf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } log = { version = "0.4.19", "features" = ["std"] } hex = "0.4.2" -reqwest = { version = "0.11.9", features = ["json"]} +reqwest = { version = "0.11.9", features = ["json", "rustls-tls"] } base64 = "0.21.2" url = { version = "2.2.2", features = ["serde"] } derive_more = "0.99.11" @@ -44,12 +44,4 @@ web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMo wasm-bindgen-test = { version = "0.3.2" } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.28.2", features = ["rt", "macros"] } - - -# TODO; choose between rustls-tls or native-tls for linux target -# for linux target, this requires an additional dep, `libssl-dev`(debian) -# either install libssl-dev in all CI's or use rustls-tls -# All other targets use the system's default ssl implementation -[target.x86_64-unknown-linux-gnu.dependencies] -reqwest = { version = "0.11.9", features = ["json", "rustls-tls"] } \ No newline at end of file +tokio = { version = "1.28.2", features = ["rt", "macros"] } \ No newline at end of file From 5c6738384e27698cdf4f085ccaae13b233d980ba Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 14 Oct 2024 13:57:06 -0400 Subject: [PATCH 104/233] use native rust ssl for all targets; linux target would require libssl-dev system dep otherwise --- Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3188bf9..2fd2cc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } log = { version = "0.4.19", "features" = ["std"] } hex = "0.4.2" -reqwest = { version = "0.11.9", features = ["json", "rustls-tls"] } +reqwest = { version = "0.11.9", features = ["json", "rustls-tls"], default-features = false } base64 = "0.21.2" url = { version = "2.2.2", features = ["serde"] } derive_more = "0.99.11" @@ -44,4 +44,6 @@ web-sys = { version = "0.3.55", features = ["Request", "RequestInit", "RequestMo wasm-bindgen-test = { version = "0.3.2" } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.28.2", features = ["rt", "macros"] } \ No newline at end of file +tokio = { version = "1.28.2", features = ["rt", "macros"] } + + From 0edf6a72e6e7d1f6195c10160a87a981c9792cc6 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 16 Oct 2024 22:09:40 -0400 Subject: [PATCH 105/233] remove unnecessary new type wrapper --- src/transport/endpoints.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index dfbb8ee..899cfce 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -120,7 +120,7 @@ pub struct AddressBalanceResponse { /// - [Go Source for Hash256](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L63) /// /// # Response -/// - The response is a `GetEventResponse` in Rust, corresponding to `types.Event` in Go. +/// - The response is an `Event` in Rust, corresponding to `types.Event` in Go. /// - [Go Source for Event](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/wallet/wallet.go#L14) /// /// # References @@ -133,7 +133,7 @@ pub struct GetEventRequest { } impl SiaApiRequest for GetEventRequest { - type Response = GetEventResponse; + type Response = Event; fn to_endpoint_schema(&self) -> Result { // Create the path_params HashMap to substitute {txid} in the path schema @@ -148,9 +148,6 @@ impl SiaApiRequest for GetEventRequest { } } -#[derive(Debug, Deserialize, Serialize)] -pub struct GetEventResponse(pub Event); - /// Represents the request-response pair for fetching events for a specific address. /// /// # Walletd Endpoint From c11f697d48ba0cffdbd01e01109ee1a3fca5cd50 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 16 Oct 2024 22:12:06 -0400 Subject: [PATCH 106/233] impl Clone for all endpoint request and response types --- src/transport/endpoints.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 899cfce..17e0424 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -40,7 +40,7 @@ pub trait SiaApiRequest: Send { /// - [Go Source for the ChainIndex Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L194) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct ConsensusTipRequest; impl SiaApiRequest for ConsensusTipRequest { @@ -51,7 +51,7 @@ impl SiaApiRequest for ConsensusTipRequest { } } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct ConsensusTipResponse { pub height: u64, pub id: BlockID, @@ -80,7 +80,7 @@ pub struct ConsensusTipResponse { /// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/api/server.go#L752) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct AddressBalanceRequest { pub address: Address, } @@ -100,7 +100,7 @@ impl SiaApiRequest for AddressBalanceRequest { } } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct AddressBalanceResponse { pub siacoins: Currency, #[serde(rename = "immatureSiacoins")] @@ -127,7 +127,7 @@ pub struct AddressBalanceResponse { /// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/134a28b063df60a687899ac33aa373bf461480bc/api/server.go#L828) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct GetEventRequest { pub txid: Hash256, } @@ -169,7 +169,7 @@ impl SiaApiRequest for GetEventRequest { /// - [Go Source for the Event Object](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/wallet/wallet.go#L14) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct AddressesEventsRequest { pub address: Address, pub limit: Option, @@ -224,7 +224,7 @@ pub type AddressesEventsResponse = Vec; /// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/api/server.go#L795) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct GetAddressUtxosRequest { pub address: Address, pub limit: Option, @@ -286,13 +286,13 @@ impl SiaApiRequest for GetAddressUtxosRequest { /// - [Go Source for the V2Transaction Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L649) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct TxpoolBroadcastRequest { pub transactions: Vec, pub v2transactions: Vec, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct EmptyResponse; impl SiaApiRequest for TxpoolBroadcastRequest { @@ -335,10 +335,10 @@ impl SiaApiRequest for TxpoolBroadcastRequest { /// - [Go Source for the Currency Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/currency.go#L26) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct TxpoolFeeRequest; -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct TxpoolFeeResponse(pub Currency); impl SiaApiRequest for TxpoolFeeRequest { @@ -367,7 +367,7 @@ impl SiaApiRequest for TxpoolFeeRequest { /// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/api/server.go#L282C18-L282C43) /// /// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct TxpoolTransactionsRequest; impl SiaApiRequest for TxpoolTransactionsRequest { From fe8c86ee5d1b6b9f5e480aa4111ec85e456462c8 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 19 Oct 2024 19:29:38 -0400 Subject: [PATCH 107/233] remove GetAddressUtxosResponse type alias --- src/transport/endpoints.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 17e0424..8353baf 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -231,10 +231,8 @@ pub struct GetAddressUtxosRequest { pub offset: Option, } -pub type GetAddressUtxosResponse = Vec; - impl SiaApiRequest for GetAddressUtxosRequest { - type Response = GetAddressUtxosResponse; + type Response = Vec; fn to_endpoint_schema(&self) -> Result { let mut path_params = HashMap::new(); From 09fa07bcd3853cdf99749edab9ec73d35cd31b73 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 19 Oct 2024 19:30:05 -0400 Subject: [PATCH 108/233] add Currency doc comment and TODO; add SiacoinElement doc comment --- src/types/transaction.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 04ed5b6..62554bc 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -10,6 +10,10 @@ use std::str::FromStr; const V2_REPLAY_PREFIX: u8 = 2; +/// A currency amount in the Sia network represented in Hastings, the smallest unit of currency. +/// 1 SC = 10^24 Hastings +/// use to_string_hastings() or to_string_siacoin() to display the value.\ +// TODO Alright impl Add, Sub, PartialOrd, etc #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct Currency(pub u128); @@ -213,6 +217,12 @@ impl Encodable for SiafundElement { } } + +/// As per, Sia Core a "SiacoinElement is a record of a SiacoinOutput within the state accumulator." +/// This type is effectively a "UTXO" in Bitcoin terms. +/// A SiacoinElement can be combined with a SatisfiedPolicy to create a SiacoinInputV2. +/// Ported from Sia Core: +/// https://github.com/SiaFoundation/core/blob/b7ccbe54cccba5642c2bb9d721967214a4ba4e97/types/types.go#L619 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiacoinElement { From ee4bc016e6ee59911a499c9ff06ce433ec2f076b Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 06:21:27 -0400 Subject: [PATCH 109/233] add api/debug/mine endpoint --- src/transport/endpoints.rs | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 8353baf..7e682da 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -12,6 +12,7 @@ const ENDPOINT_EVENTS: &str = "api/events/{txid}"; const ENDPOINT_TXPOOL_BROADCAST: &str = "api/txpool/broadcast"; const ENDPOINT_TXPOOL_FEE: &str = "api/txpool/fee"; const ENDPOINT_TXPOOL_TRANSACTIONS: &str = "api/txpool/transactions"; +const ENDPOINT_DEBUG_MINE: &str = "api/debug/mine"; pub trait SiaApiRequest: Send { type Response: DeserializeOwned; @@ -379,3 +380,47 @@ impl SiaApiRequest for TxpoolTransactionsRequest { ) } } + +/// Represents the request-response pair for mining blocks on a Sia node. +/// +/// # Walletd Endpoint +/// `POST api/debug/mine` +/// +/// # Description +/// Mine n blocks to a specified address. This is a debug endpoint intended to be used on CI/CD test networks only. +/// This method is only supported on walletd nodes started with `-debug` flag. +/// +/// # Fields +/// - `address`: The address where blocks will be mined to. (`types.Address` type in Go) +/// - [Go Source for Address Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L165) +/// - `blocks`: The amount of blocks to mine. (`int` type in Go) +/// +/// # Response +/// - The response is `HTTP 204 NO CONTENT`, which is represented by `EmptyResponse` in Rust. +/// This indicates that the request was successful but there is no response body. +/// +/// # References +/// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/1e56661fa23bb39438ec869c91d661d51bc889a4/api/server.go#L872) +/// +/// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct DebugMineRequest { + pub address: Address, + pub blocks: i64, +} + +impl SiaApiRequest for DebugMineRequest { + type Response = EmptyResponse; + + fn is_empty_response() -> Option { Some(EmptyResponse) } + + fn to_endpoint_schema(&self) -> Result { + // Serialize the request into a JSON string + let body = serde_json::to_string(self).map_err(ApiClientError::Serde)?; + Ok( + EndpointSchemaBuilder::new(ENDPOINT_DEBUG_MINE.to_owned(), SchemaMethod::Post) + .body(Body::Utf8(body)) // Set the JSON body for the POST request + .build(), + ) + } +} \ No newline at end of file From 068ff9432f238aaacd02a2caf1aec7c4339ca1ce Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 06:21:49 -0400 Subject: [PATCH 110/233] edit endpoint doc comments --- src/transport/endpoints.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 7e682da..a6df5ab 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -156,8 +156,8 @@ impl SiaApiRequest for GetEventRequest { /// /// # Fields /// - `addr`: (`types.Address` in Go) the address for which events are fetched. -/// - `limit`: (`i64` in Go) optional limit for the number of results. -/// - `offset`: (`i64` in Go) optional offset for paginated results. +/// - `limit`: (`int` type in Go) optional limit for the number of results. +/// - `offset`: (`int` type in Go) optional offset for paginated results. /// /// # Response /// - `[]types.Event` in Go corresponds to `Vec` in Rust. @@ -214,8 +214,8 @@ pub type AddressesEventsResponse = Vec; /// # Fields /// - `address`: The address for which to fetch UTXOs. In Go, this corresponds to `types.Address`. /// - [Go Source for Address Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L165) -/// - `limit`: An optional limit on the number of results. Corresponds to `i64` in Go. -/// - `offset`: An optional offset for paginated results. Corresponds to `i64` in Go. +/// - `limit`: An optional limit on the number of results. Corresponds to `int64` in Go. +/// - `offset`: An optional offset for paginated results. Corresponds to `int64` in Go. /// /// # Response /// - The response is a `Vec` in Rust, corresponding to `[]types.SiacoinElement` in Go. From 0e1e4c43e1ad3b13640d5b6673026c014e1584a0 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 08:52:34 -0400 Subject: [PATCH 111/233] add clarification to debug comment --- src/types/transaction.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 62554bc..72bd833 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1248,7 +1248,8 @@ impl V2TransactionBuilder { } // input is a special case becuase we cannot generate signatures until after fully constructing the transaction - // only the parent field is utilized while encoding the transaction to calculate the signature hash + // because of this only the parent field is utilized while encoding the transaction to calculate the signature hash + // satisfied_policy effectively acts as a placeholder in this function pub fn add_siacoin_input(mut self, parent: SiacoinElement, policy: SpendPolicy) -> Self { self.siacoin_inputs.push(SiacoinInputV2 { parent, From 83edc285f2a99712eb59f6f8243dd49844a5178d Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 09:05:42 -0400 Subject: [PATCH 112/233] move ApiClientHelpers to its own module --- src/transport/client.rs | 18 ++++-------------- src/transport/client/helpers.rs | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 src/transport/client/helpers.rs diff --git a/src/transport/client.rs b/src/transport/client.rs index ab48f06..8355132 100644 --- a/src/transport/client.rs +++ b/src/transport/client.rs @@ -1,6 +1,4 @@ -use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, SiaApiRequest}; - -use crate::types::Address; +use crate::transport::endpoints::SiaApiRequest; use async_trait::async_trait; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use serde_json::Value as JsonValue; @@ -11,6 +9,9 @@ use url::Url; #[cfg(not(target_arch = "wasm32"))] pub mod native; #[cfg(target_arch = "wasm32")] pub mod wasm; +mod helpers; +pub use helpers::ApiClientHelpers; + // FIXME remove these client specific error types #[cfg(not(target_arch = "wasm32"))] use reqwest::Error as ReqwestError; @@ -44,17 +45,6 @@ pub trait ApiClient: Clone { async fn dispatcher(&self, request: R) -> Result; } -#[async_trait] -pub trait ApiClientHelpers: ApiClient { - async fn current_height(&self) -> Result { - Ok(self.dispatcher(ConsensusTipRequest).await?.height) - } - - async fn address_balance(&self, address: Address) -> Result { - self.dispatcher(AddressBalanceRequest { address }).await - } -} - #[derive(Debug, Error)] pub enum ApiClientError { #[error("BuildError error: {0}")] diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs new file mode 100644 index 0000000..ef67dca --- /dev/null +++ b/src/transport/client/helpers.rs @@ -0,0 +1,25 @@ +use super::{ApiClient, ApiClientError}; +use crate::transport::endpoints::{AddressBalanceRequest, ConsensusTipRequest, GetAddressUtxosRequest, AddressBalanceResponse}; +use crate::types::{Address, Currency, SiacoinElement}; +use async_trait::async_trait; + + +#[async_trait] +pub trait ApiClientHelpers: ApiClient { + async fn current_height(&self) -> Result { + Ok(self.dispatcher(ConsensusTipRequest).await?.height) + } + + async fn address_balance(&self, address: Address) -> Result { + self.dispatcher(AddressBalanceRequest { address }).await + } + + async fn get_unspent_outputs(&self, address: &Address, limit: Option, offset: Option) -> Result, ApiClientError> { + self.dispatcher(GetAddressUtxosRequest { address: address.clone(), limit, offset }).await + } + + async fn select_utxos(&self, address: &Address, amount: Currency) -> Result, ApiClientError> { + let _utxos = self.get_unspent_outputs(address, None, None).await?; + todo!() + } +} \ No newline at end of file From b6bb7ebe2e8d5b5c27f591f29ad30b232ba4f05b Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 09:06:06 -0400 Subject: [PATCH 113/233] add Address::from_public_key --- src/types.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types.rs b/src/types.rs index 577272d..34547b0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -86,6 +86,10 @@ impl Address { let hash = standard_unlock_hash(pubkey); Address(hash) } + + pub fn from_public_key(pubkey: &PublicKey) -> Self { + SpendPolicy::PublicKey(pubkey.clone()).address() + } } impl Encodable for Address { From 432f1d7186a111a8bf039863325b28649f9ad25a Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 09:41:40 -0400 Subject: [PATCH 114/233] make Currency::ZERO const pub --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 72bd833..ded965e 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -24,7 +24,7 @@ impl Deref for Currency { } impl Currency { - const ZERO: Currency = Currency(0); + pub const ZERO: Currency = Currency(0); } // TODO does this also need to be able to deserialize from an integer? From d7eb095e1955ac1355ad56fc064e76c8c1e90e4b Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 20 Oct 2024 09:42:04 -0400 Subject: [PATCH 115/233] add ApiClientHelpers::select_unspent_outputs --- src/transport/client/helpers.rs | 59 +++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index ef67dca..d59f98a 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -2,8 +2,22 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, ConsensusTipRequest, GetAddressUtxosRequest, AddressBalanceResponse}; use crate::types::{Address, Currency, SiacoinElement}; use async_trait::async_trait; +use thiserror::Error; +#[derive(Debug, Error)] +pub enum ApiClientHelpersError { + #[error("ApiClientHelpersError::SelectOutputs: insufficent amount, available: {:?} required: {:?}", .available, .required)] + SelectOutputs { + available: Currency, + required: Currency, + }, + #[error("ApiClientHelpersError::ApiClientError: {0}")] + ApiClientError(#[from] ApiClientError), +} +/// Helper methods for the ApiClient trait +/// These generally provide higher level functionality than the base ApiClient trait +/// This crate is focused on catering to the Komodo Defi Framework integration #[async_trait] pub trait ApiClientHelpers: ApiClient { async fn current_height(&self) -> Result { @@ -18,8 +32,47 @@ pub trait ApiClientHelpers: ApiClient { self.dispatcher(GetAddressUtxosRequest { address: address.clone(), limit, offset }).await } - async fn select_utxos(&self, address: &Address, amount: Currency) -> Result, ApiClientError> { - let _utxos = self.get_unspent_outputs(address, None, None).await?; - todo!() + /// Fetches unspent outputs for the given address and attemopts to select a subset of outputs + /// whose total value is at least `total_amount`. The outputs are sorted from largest to smallest to minimize + /// the number of outputs selected. The function returns a vector of the selected outputs and the difference between + /// the total value of the selected outputs and the required amount, aka the change. + /// # Arguments + /// + /// * `unspent_outputs` - A vector of `SiacoinElement`s representing unspent Siacoin outputs. + /// * `total_amount` - The total amount (in u128) required for the selection. + /// + /// # Returns + /// + /// This function returns `Result<(Vec, Currency), ApiClientHelpersError>`: + /// * `Ok((Vec, Currency))` - A tuple containing, a vector of the selected unspent outputs and the change amount. + /// * `Err(MmError)` - An error is returned if the available outputs cannot meet the required amount or a transport error is encountered. + async fn select_unspent_outputs(&self, address: &Address, total_amount: Currency) -> Result<(Vec, Currency), ApiClientHelpersError> { + let mut unspent_outputs = self.get_unspent_outputs(address, None, None).await?; + + // Sort outputs from largest to smallest + unspent_outputs.sort_by(|a, b| b.siacoin_output.value.0.cmp(&a.siacoin_output.value.0)); + + let mut selected = Vec::new(); + let mut selected_amount = 0; + + // Select outputs until the total amount is reached + for output in unspent_outputs { + selected_amount += *output.siacoin_output.value; + selected.push(output); + + if selected_amount >= *total_amount { + break; + } + } + + if selected_amount < *total_amount { + return Err(ApiClientHelpersError::SelectOutputs { + available: selected_amount.into(), + required: total_amount.into(), + }); + } + let change = selected_amount as u128 - *total_amount; + + Ok((selected, change.into())) } } \ No newline at end of file From 75565e6bfacfd8575fa18399c9d1bf900ef782fa Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 21:49:55 -0400 Subject: [PATCH 116/233] impl FromStr for PublicKey in keypair.rs --- src/types/keypair.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 33a9521..e1a801b 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -3,6 +3,7 @@ use ed25519_dalek::{ExpandedSecretKey, PublicKey as Ed25519PublicKey, SecretKey, SignatureError as Ed25519SignatureError, Signer, Verifier, SECRET_KEY_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; +use std::str::FromStr; use thiserror::Error; use crate::types::{Address, SpendPolicy, Signature}; @@ -130,6 +131,18 @@ impl PublicKey { } } +impl FromStr for PublicKey { + type Err = PublicKeyError; + + fn from_str(s: &str) -> Result { + if let Some(hex_str) = s.strip_prefix("ed25519:") { + PublicKey::from_str_no_prefix(hex_str) + } else { + Err(PublicKeyError::InvalidHex(s.to_string())) + } + } +} + impl<'de> Deserialize<'de> for PublicKey { fn deserialize(deserializer: D) -> Result where From 13e54c7284ed6995fb6dc7c266f7b933a3444f22 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 21:50:10 -0400 Subject: [PATCH 117/233] doc comment typo --- src/transport/client/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index d59f98a..0bc9f0b 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -32,7 +32,7 @@ pub trait ApiClientHelpers: ApiClient { self.dispatcher(GetAddressUtxosRequest { address: address.clone(), limit, offset }).await } - /// Fetches unspent outputs for the given address and attemopts to select a subset of outputs + /// Fetches unspent outputs for the given address and attempts to select a subset of outputs /// whose total value is at least `total_amount`. The outputs are sorted from largest to smallest to minimize /// the number of outputs selected. The function returns a vector of the selected outputs and the difference between /// the total value of the selected outputs and the required amount, aka the change. From 4b9b63fba2abd8ec151594b15664f5921944f8fb Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:01:47 -0400 Subject: [PATCH 118/233] use public_key impl of Verifier instead of duplicate impl on Keypair --- src/types/keypair.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index e1a801b..bca1674 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -40,13 +40,6 @@ impl Signer for Keypair { } } -impl Verifier for Keypair { - /// Verify a signature on a message with this keypair's public key. - fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { - self.public.0.verify(message, &signature.0) - } -} - impl Keypair { pub fn from_private_bytes(bytes: &[u8]) -> Result { let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; @@ -57,8 +50,9 @@ impl Keypair { pub fn sign(&self, message: &[u8]) -> Signature { Signer::sign(self, message) } - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { - Verifier::verify(self, message, signature) + /// Verify a signature of a message with this keypair's public key. + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { + self.public.verify(message, signature) } pub fn public(&self) -> PublicKey { From 17df012928fc29459ee6d13185e35772e23b6fdd Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:07:50 -0400 Subject: [PATCH 119/233] split KeypairError into PublicKeyError and PrivateKeyError --- src/types/keypair.rs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index bca1674..971aaca 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -6,20 +6,23 @@ use std::fmt; use std::str::FromStr; use thiserror::Error; -use crate::types::{Address, SpendPolicy, Signature}; +use crate::types::{Address, SpendPolicy, Signature, SignatureError}; #[derive(Debug, Error)] -pub enum KeypairError { - #[error("invalid secret key: {0}")] - InvalidSecretKey(Ed25519SignatureError), +pub enum PublicKeyError { #[error("invalid public key length: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] - PublicKeyInvalidLength(String), - #[error("public key invalid hex: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] - PublicKeyInvalidHex(String), + InvalidLength(String), + #[error("public key invalid hex: expected 32 byte hex string, found {0}")] + InvalidHex(String), #[error("public key invalid: corrupt curve point {0}")] - PublicKeyCorruptPoint(String), + CorruptPoint(String), #[error("public key invalid: from_bytes failed {0}")] - PublicKeyParseBytes(Ed25519SignatureError), + ParseBytes(Ed25519SignatureError), +} +#[derive(Debug, Error)] +pub enum PrivateKeyError { + #[error("invalid private key: from_bytes failed {0}")] + InvalidPrivateKey(Ed25519SignatureError), } /// A Sia Public-Private Keypair @@ -41,8 +44,8 @@ impl Signer for Keypair { } impl Keypair { - pub fn from_private_bytes(bytes: &[u8]) -> Result { - let secret = SecretKey::from_bytes(bytes).map_err(KeypairError::InvalidSecretKey)?; + pub fn from_private_bytes(bytes: &[u8]) -> Result { + let secret = SecretKey::from_bytes(bytes).map_err(PrivateKeyError::InvalidPrivateKey)?; let public = PublicKey(Ed25519PublicKey::from(&secret)); let private = PrivateKey(secret); Ok(Keypair { public, private }) @@ -70,21 +73,21 @@ struct PrivateKey(SecretKey); pub struct PublicKey(pub Ed25519PublicKey); impl Verifier for PublicKey { - /// Verify a signature on a message with this keypair's public key. + /// Verify a signature of a message with this keypair's public key. fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { self.0.verify(message, &signature.0) } } impl PublicKey { - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { let public_key = Ed25519PublicKey::from_bytes(bytes) .map(PublicKey) - .map_err(KeypairError::PublicKeyParseBytes)?; + .map_err(PublicKeyError::ParseBytes)?; match public_key.validate_point() { true => Ok(public_key), - false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), + false => Err(PublicKeyError::CorruptPoint(hex::encode(bytes))), } } @@ -101,16 +104,16 @@ impl PublicKey { pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } // Method for parsing a hex string without the "ed25519:" prefix - pub fn from_str_no_prefix(hex_str: &str) -> Result { + pub fn from_str_no_prefix(hex_str: &str) -> Result { let mut bytes = [0u8; 32]; hex::decode_to_slice(hex_str, &mut bytes) - .map_err(|_| KeypairError::PublicKeyInvalidHex(hex_str.to_string()))?; + .map_err(|_| PublicKeyError::InvalidHex(hex_str.to_string()))?; let public_key = Self::from_bytes(&bytes)?; match public_key.validate_point() { true => Ok(public_key), - false => Err(KeypairError::PublicKeyCorruptPoint(hex::encode(bytes))), + false => Err(PublicKeyError::CorruptPoint(hex::encode(bytes))), } } From 90c68c70cdae5f76534be80ee0ac729f50eeddd1 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:08:21 -0400 Subject: [PATCH 120/233] impl verify signature method for PublicKey --- src/types/keypair.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 971aaca..02443c3 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -126,6 +126,10 @@ impl PublicKey { pub fn address(&self) -> Address { SpendPolicy::PublicKey(self.clone()).address() } + + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { + Verifier::verify(self, message, signature).map_err(SignatureError::VerifyFailed) + } } impl FromStr for PublicKey { From 1de37ce405b784b25649723f3d74869b5f12845e Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:09:16 -0400 Subject: [PATCH 121/233] pub expose PublicKeyError and PrivateKeyError in types module --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 34547b0..30b4db4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -17,7 +17,7 @@ mod signature; pub use signature::{Signature, SignatureError}; mod keypair; -pub use keypair::{Keypair, KeypairError, PublicKey}; +pub use keypair::{Keypair, PublicKeyError, PublicKey, PrivateKeyError}; mod spend_policy; pub use spend_policy::*; From bbb0b551f0daf5d16924dcd52ada269cac5c269d Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:13:05 -0400 Subject: [PATCH 122/233] add SignatureError::VerifyFailed variant; edit display impls of SignatureError --- src/types/signature.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/types/signature.rs b/src/types/signature.rs index 82168a0..d357b2c 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -8,12 +8,14 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum SignatureError { - #[error("parsing error: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] + #[error("signature parsing failed: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] Parse(#[from] ed25519_dalek::ed25519::Error), - #[error("invalid prefix: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] + #[error("invalid signature prefix: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] InvalidPrefix(String), - #[error("corrupt R point: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] + #[error("invalid signature, corrupt R point: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] CorruptRPoint(String), + #[error("signature verification failed: {0}")] + VerifyFailed(Ed25519SignatureError), } #[derive(Clone, Debug, PartialEq)] From 2520c46e8a9add7bdaddee0ec973094b84d2916c Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:13:31 -0400 Subject: [PATCH 123/233] add Signature::verify method --- src/types/signature.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/types/signature.rs b/src/types/signature.rs index d357b2c..9e1811c 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -1,6 +1,8 @@ +use crate::types::keypair::PublicKey; + use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::ed25519::signature::{Error as SignatureCrateError, Signature as SignatureTrait}; -use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; +use ed25519_dalek::{Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, SIGNATURE_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; @@ -69,6 +71,10 @@ impl Signature { // Create a CompressedEdwardsY point from the first 32 bytes CompressedEdwardsY::from_slice(r_bytes).decompress().is_some() } + + pub fn verify(&self, message: &[u8], public_key: &PublicKey) -> Result<(), SignatureError> { + public_key.verify(message, &self) + } } impl<'de> Deserialize<'de> for Signature { From 013725435dd96016f74c069f8880b1e463fdcd99 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:14:37 -0400 Subject: [PATCH 124/233] derive V1TransactionSansSigs Deref; remove manual Deref impl --- src/types/transaction.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index ded965e..838a8c7 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -944,15 +944,9 @@ impl Encodable for SiafundInputV1 { } } // TODO possible this can just hold a ref to V1Transaction like CurrencyVersion -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deref, Deserialize, Serialize)] pub struct V1TransactionSansSigs(V1Transaction); -impl Deref for V1TransactionSansSigs { - type Target = V1Transaction; - - fn deref(&self) -> &Self::Target { &self.0 } -} - impl Encodable for V1TransactionSansSigs { fn encode(&self, encoder: &mut Encoder) { encoder.write_len_prefixed_vec(&self.siacoin_inputs); From 4848dc0e559c01a756528229be1ea899904bb09b Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:15:10 -0400 Subject: [PATCH 125/233] derive arithmetic traits for Currency type via derive_more crate --- src/types/transaction.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 838a8c7..b966e00 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1,11 +1,11 @@ use crate::encoding::{Encodable, Encoder}; use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature, SpendPolicy, UnlockCondition, UnlockKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use derive_more::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign, Display, From, Into, Sum, Deref}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use std::convert::{TryFrom, TryInto}; use std::fmt; -use std::ops::Deref; use std::str::FromStr; const V2_REPLAY_PREFIX: u8 = 2; @@ -14,15 +14,9 @@ const V2_REPLAY_PREFIX: u8 = 2; /// 1 SC = 10^24 Hastings /// use to_string_hastings() or to_string_siacoin() to display the value.\ // TODO Alright impl Add, Sub, PartialOrd, etc -#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[derive(Copy, Clone, Debug, Deref, Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign, PartialEq, Eq, PartialOrd, Ord, Display, Default, From, Into, Sum)] pub struct Currency(pub u128); -impl Deref for Currency { - type Target = u128; - - fn deref(&self) -> &Self::Target { &self.0 } -} - impl Currency { pub const ZERO: Currency = Currency(0); } From 01c9cd4d4ac4720ab1623f7be5e32b90ba8fa183 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:16:06 -0400 Subject: [PATCH 126/233] add Currency::COIN const --- src/types/transaction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index b966e00..242f619 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -19,6 +19,8 @@ pub struct Currency(pub u128); impl Currency { pub const ZERO: Currency = Currency(0); + + pub const COIN: Currency = Currency(1e24 as u128); } // TODO does this also need to be able to deserialize from an integer? From 6405fc068293eb5c08131ebdba425b385e8c0c7e Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:16:53 -0400 Subject: [PATCH 127/233] remove From and From for Currency --- src/types/transaction.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 242f619..ce1cbf8 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -64,14 +64,6 @@ impl From for Currency { fn from(value: u64) -> Self { Currency(value.into()) } } -impl From for Currency { - fn from(value: i32) -> Self { Currency(value as u128) } -} - -impl From for Currency { - fn from(value: u128) -> Self { Currency(value) } -} - // Currency remains the same data structure between V1 and V2 however the encoding changes #[derive(Clone, Debug)] pub enum CurrencyVersion<'a> { From 95298ede0f2b18624aae5d3efe213bdebf26b845 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 21 Oct 2024 22:18:17 -0400 Subject: [PATCH 128/233] add comment re: TxBuilder input signing --- src/types/transaction.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index ce1cbf8..5a533eb 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -898,7 +898,8 @@ impl Encodable for V1ArbitraryData { } } /* -While implementing this, we faced two options. +While implementing +, we faced two options. 1.) Treat every field as an Option<> 2.) Always initialize every empty field as a Vec<> @@ -1229,9 +1230,12 @@ impl V2TransactionBuilder { self } - // input is a special case becuase we cannot generate signatures until after fully constructing the transaction - // because of this only the parent field is utilized while encoding the transaction to calculate the signature hash - // satisfied_policy effectively acts as a placeholder in this function + /* Input is a special case becuase we cannot generate signatures until after fully constructing + the transaction. Only the parent field is utilized while encoding the transaction to + calculate the signature hash. + Policy is included here to give any signing function or method a schema for producing a + signature for the input. Do not use this method if you are manually creating SatisfiedPolicys. + Use siacoin_inputs() to add fully formed inputs instead. */ pub fn add_siacoin_input(mut self, parent: SiacoinElement, policy: SpendPolicy) -> Self { self.siacoin_inputs.push(SiacoinInputV2 { parent, From 0aad6ce93d1a5b7f5d32aa0a4396d4c23c8b8a84 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 04:54:08 -0400 Subject: [PATCH 129/233] update V2TransactionBuilder methods to use ref instead for easier for loop/iter handling --- src/types/transaction.rs | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 5a533eb..e071c58 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1076,17 +1076,17 @@ impl Encodable for V2Transaction { } pub struct V2TransactionBuilder { - siacoin_inputs: Vec, - siacoin_outputs: Vec, - siafund_inputs: Vec, - siafund_outputs: Vec, - file_contracts: Vec, - file_contract_revisions: Vec, - file_contract_resolutions: Vec, - attestations: Vec, - arbitrary_data: Vec, - new_foundation_address: Option
, - miner_fee: Currency, + pub siacoin_inputs: Vec, + pub siacoin_outputs: Vec, + pub siafund_inputs: Vec, + pub siafund_outputs: Vec, + pub file_contracts: Vec, + pub file_contract_revisions: Vec, + pub file_contract_resolutions: Vec, + pub attestations: Vec, + pub arbitrary_data: Vec, + pub new_foundation_address: Option
, + pub miner_fee: Currency, } impl V2TransactionBuilder { @@ -1175,57 +1175,57 @@ impl V2TransactionBuilder { } } - pub fn siacoin_inputs(mut self, inputs: Vec) -> Self { + pub fn siacoin_inputs(&mut self, inputs: Vec) -> &mut Self { self.siacoin_inputs = inputs; self } - pub fn siacoin_outputs(mut self, outputs: Vec) -> Self { + pub fn siacoin_outputs(&mut self, outputs: Vec) -> &mut Self { self.siacoin_outputs = outputs; self } - pub fn siafund_inputs(mut self, inputs: Vec) -> Self { + pub fn siafund_inputs(&mut self, inputs: Vec) -> &mut Self { self.siafund_inputs = inputs; self } - pub fn siafund_outputs(mut self, outputs: Vec) -> Self { + pub fn siafund_outputs(&mut self, outputs: Vec) -> &mut Self { self.siafund_outputs = outputs; self } - pub fn file_contracts(mut self, contracts: Vec) -> Self { + pub fn file_contracts(&mut self, contracts: Vec) -> &mut Self { self.file_contracts = contracts; self } - pub fn file_contract_revisions(mut self, revisions: Vec) -> Self { + pub fn file_contract_revisions(&mut self, revisions: Vec) -> &mut Self { self.file_contract_revisions = revisions; self } - pub fn file_contract_resolutions(mut self, resolutions: Vec) -> Self { + pub fn file_contract_resolutions(&mut self, resolutions: Vec) -> &mut Self { self.file_contract_resolutions = resolutions; self } - pub fn attestations(mut self, attestations: Vec) -> Self { + pub fn attestations(&mut self, attestations: Vec) -> &mut Self { self.attestations = attestations; self } - pub fn arbitrary_data(mut self, data: Vec) -> Self { + pub fn arbitrary_data(&mut self, data: Vec) -> &mut Self { self.arbitrary_data = data; self } - pub fn new_foundation_address(mut self, address: Address) -> Self { + pub fn new_foundation_address(&mut self, address: Address) -> &mut Self { self.new_foundation_address = Some(address); self } - pub fn miner_fee(mut self, fee: Currency) -> Self { + pub fn miner_fee(&mut self, fee: Currency) -> &mut Self { self.miner_fee = fee; self } @@ -1236,7 +1236,7 @@ impl V2TransactionBuilder { Policy is included here to give any signing function or method a schema for producing a signature for the input. Do not use this method if you are manually creating SatisfiedPolicys. Use siacoin_inputs() to add fully formed inputs instead. */ - pub fn add_siacoin_input(mut self, parent: SiacoinElement, policy: SpendPolicy) -> Self { + pub fn add_siacoin_input(&mut self, parent: SiacoinElement, policy: SpendPolicy) -> &mut Self { self.siacoin_inputs.push(SiacoinInputV2 { parent, satisfied_policy: SatisfiedPolicy { @@ -1248,7 +1248,7 @@ impl V2TransactionBuilder { self } - pub fn add_siacoin_output(mut self, output: SiacoinOutput) -> Self { + pub fn add_siacoin_output(&mut self, output: SiacoinOutput) -> &mut Self { self.siacoin_outputs.push(output); self } @@ -1263,7 +1263,7 @@ impl V2TransactionBuilder { // Sign all PublicKey or UnlockConditions policies with the provided keypairs // Incapable of handling threshold policies - pub fn sign_simple(mut self, keypairs: Vec<&Keypair>) -> Result { + pub fn sign_simple(mut self, keypairs: Vec<&Keypair>) -> Self { let sig_hash = self.input_sig_hash(); for keypair in keypairs { let sig = keypair.sign(&sig_hash.0); @@ -1286,7 +1286,7 @@ impl V2TransactionBuilder { } } } - Ok(self) + self } pub fn build(self) -> V2Transaction { From a065cf02e454982eaf7668f4047851f4b5e74d86 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 04:54:30 -0400 Subject: [PATCH 130/233] add From for SiacoinOutput helpers --- src/types/transaction.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index e071c58..30d2a88 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -386,6 +386,14 @@ pub struct SiacoinOutput { pub address: Address, } +impl From<(Currency, Address)> for SiacoinOutput { + fn from(tuple: (Currency, Address)) -> Self { SiacoinOutput { value: tuple.0, address: tuple.1 } } +} + +impl From<(Address, Currency)> for SiacoinOutput { + fn from(tuple: (Address, Currency)) -> Self { SiacoinOutput { address: tuple.0, value: tuple.1 } } +} + #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] #[serde(default)] From 7a212e7439351b7b9eedcc9f3b0aea22ef6af723 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 04:54:56 -0400 Subject: [PATCH 131/233] add Currency::DUST const placeholder --- src/types/transaction.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 30d2a88..65531a6 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -21,6 +21,10 @@ impl Currency { pub const ZERO: Currency = Currency(0); pub const COIN: Currency = Currency(1e24 as u128); + + /// The minimum amount of currency for a transaction output + // FIXME this is a placeholder value until testing is complete + pub const DUST: Currency = Currency(1_000_000); } // TODO does this also need to be able to deserialize from an integer? From ce5ad5d5e53d188b3df2dd1fb5ce35b2dc6d7a48 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 04:55:17 -0400 Subject: [PATCH 132/233] make ApiClientHelpersError pub --- src/transport/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client.rs b/src/transport/client.rs index 8355132..0024302 100644 --- a/src/transport/client.rs +++ b/src/transport/client.rs @@ -10,7 +10,7 @@ use url::Url; #[cfg(target_arch = "wasm32")] pub mod wasm; mod helpers; -pub use helpers::ApiClientHelpers; +pub use helpers::{ApiClientHelpers, ApiClientHelpersError}; // FIXME remove these client specific error types #[cfg(not(target_arch = "wasm32"))] From cfcd6a90a02754808f2396da9996b1091244fc16 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:34:07 -0400 Subject: [PATCH 133/233] simplify ApiClientHelpersError::SelectOutputs display impl --- src/transport/client/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 0bc9f0b..51117a9 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -6,7 +6,7 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum ApiClientHelpersError { - #[error("ApiClientHelpersError::SelectOutputs: insufficent amount, available: {:?} required: {:?}", .available, .required)] + #[error("ApiClientHelpersError::SelectOutputs: insufficent amount, available: {available:?} required: {required:?}")] SelectOutputs { available: Currency, required: Currency, From b9881afa78c9b3d896fe8b0048d1d0f0593a82e4 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:34:35 -0400 Subject: [PATCH 134/233] impl Clone, Debug for V2TransactionBuilder --- src/types/transaction.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 65531a6..2e04428 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1087,6 +1087,7 @@ impl Encodable for V2Transaction { } } +#[derive(Clone, Debug)] pub struct V2TransactionBuilder { pub siacoin_inputs: Vec, pub siacoin_outputs: Vec, From 307a7aa4156f936a6a87c94d0f467d845506430f Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:35:22 -0400 Subject: [PATCH 135/233] make V2TransactionBuilder setters consume self instead of mut ref --- src/types/transaction.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 2e04428..c9d05de 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1188,57 +1188,57 @@ impl V2TransactionBuilder { } } - pub fn siacoin_inputs(&mut self, inputs: Vec) -> &mut Self { + pub fn siacoin_inputs(mut self, inputs: Vec) -> Self { self.siacoin_inputs = inputs; self } - pub fn siacoin_outputs(&mut self, outputs: Vec) -> &mut Self { + pub fn siacoin_outputs(mut self, outputs: Vec) -> Self { self.siacoin_outputs = outputs; self } - pub fn siafund_inputs(&mut self, inputs: Vec) -> &mut Self { + pub fn siafund_inputs(mut self, inputs: Vec) -> Self { self.siafund_inputs = inputs; self } - pub fn siafund_outputs(&mut self, outputs: Vec) -> &mut Self { + pub fn siafund_outputs(mut self, outputs: Vec) -> Self { self.siafund_outputs = outputs; self } - pub fn file_contracts(&mut self, contracts: Vec) -> &mut Self { + pub fn file_contracts(mut self, contracts: Vec) -> Self { self.file_contracts = contracts; self } - pub fn file_contract_revisions(&mut self, revisions: Vec) -> &mut Self { + pub fn file_contract_revisions(mut self, revisions: Vec) -> Self { self.file_contract_revisions = revisions; self } - pub fn file_contract_resolutions(&mut self, resolutions: Vec) -> &mut Self { + pub fn file_contract_resolutions(mut self, resolutions: Vec) -> Self { self.file_contract_resolutions = resolutions; self } - pub fn attestations(&mut self, attestations: Vec) -> &mut Self { + pub fn attestations(mut self, attestations: Vec) -> Self { self.attestations = attestations; self } - pub fn arbitrary_data(&mut self, data: Vec) -> &mut Self { + pub fn arbitrary_data(mut self, data: Vec) -> Self { self.arbitrary_data = data; self } - pub fn new_foundation_address(&mut self, address: Address) -> &mut Self { + pub fn new_foundation_address(mut self, address: Address) -> Self { self.new_foundation_address = Some(address); self } - pub fn miner_fee(&mut self, fee: Currency) -> &mut Self { + pub fn miner_fee(mut self, fee: Currency) -> Self { self.miner_fee = fee; self } @@ -1249,7 +1249,7 @@ impl V2TransactionBuilder { Policy is included here to give any signing function or method a schema for producing a signature for the input. Do not use this method if you are manually creating SatisfiedPolicys. Use siacoin_inputs() to add fully formed inputs instead. */ - pub fn add_siacoin_input(&mut self, parent: SiacoinElement, policy: SpendPolicy) -> &mut Self { + pub fn add_siacoin_input(mut self, parent: SiacoinElement, policy: SpendPolicy) -> Self { self.siacoin_inputs.push(SiacoinInputV2 { parent, satisfied_policy: SatisfiedPolicy { @@ -1261,7 +1261,7 @@ impl V2TransactionBuilder { self } - pub fn add_siacoin_output(&mut self, output: SiacoinOutput) -> &mut Self { + pub fn add_siacoin_output(mut self, output: SiacoinOutput) -> Self { self.siacoin_outputs.push(output); self } From 1a5fe6b53fafdc95fd2f490ff04f5e93e67b8e49 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:40:26 -0400 Subject: [PATCH 136/233] switch V2TransactionBuilder setters back to using mut ref instead of consuming self --- src/types/transaction.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index c9d05de..7faf4e8 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1188,57 +1188,57 @@ impl V2TransactionBuilder { } } - pub fn siacoin_inputs(mut self, inputs: Vec) -> Self { + pub fn siacoin_inputs(&mut self, inputs: Vec) -> &mut Self { self.siacoin_inputs = inputs; self } - pub fn siacoin_outputs(mut self, outputs: Vec) -> Self { + pub fn siacoin_outputs(&mut self, outputs: Vec) -> &mut Self { self.siacoin_outputs = outputs; self } - pub fn siafund_inputs(mut self, inputs: Vec) -> Self { + pub fn siafund_inputs(&mut self, inputs: Vec) -> &mut Self { self.siafund_inputs = inputs; self } - pub fn siafund_outputs(mut self, outputs: Vec) -> Self { + pub fn siafund_outputs(&mut self, outputs: Vec) -> &mut Self { self.siafund_outputs = outputs; self } - pub fn file_contracts(mut self, contracts: Vec) -> Self { + pub fn file_contracts(&mut self, contracts: Vec) -> &mut Self { self.file_contracts = contracts; self } - pub fn file_contract_revisions(mut self, revisions: Vec) -> Self { + pub fn file_contract_revisions(&mut self, revisions: Vec) -> &mut Self { self.file_contract_revisions = revisions; self } - pub fn file_contract_resolutions(mut self, resolutions: Vec) -> Self { + pub fn file_contract_resolutions(&mut self, resolutions: Vec) -> &mut Self { self.file_contract_resolutions = resolutions; self } - pub fn attestations(mut self, attestations: Vec) -> Self { + pub fn attestations(&mut self, attestations: Vec) -> &mut Self { self.attestations = attestations; self } - pub fn arbitrary_data(mut self, data: Vec) -> Self { + pub fn arbitrary_data(&mut self, data: Vec) -> &mut Self { self.arbitrary_data = data; self } - pub fn new_foundation_address(mut self, address: Address) -> Self { + pub fn new_foundation_address(&mut self, address: Address) -> &mut Self { self.new_foundation_address = Some(address); self } - pub fn miner_fee(mut self, fee: Currency) -> Self { + pub fn miner_fee(&mut self, fee: Currency) -> &mut Self { self.miner_fee = fee; self } @@ -1249,7 +1249,7 @@ impl V2TransactionBuilder { Policy is included here to give any signing function or method a schema for producing a signature for the input. Do not use this method if you are manually creating SatisfiedPolicys. Use siacoin_inputs() to add fully formed inputs instead. */ - pub fn add_siacoin_input(mut self, parent: SiacoinElement, policy: SpendPolicy) -> Self { + pub fn add_siacoin_input(&mut self, parent: SiacoinElement, policy: SpendPolicy) -> &mut Self { self.siacoin_inputs.push(SiacoinInputV2 { parent, satisfied_policy: SatisfiedPolicy { @@ -1261,7 +1261,7 @@ impl V2TransactionBuilder { self } - pub fn add_siacoin_output(mut self, output: SiacoinOutput) -> Self { + pub fn add_siacoin_output(&mut self, output: SiacoinOutput) -> &mut Self { self.siacoin_outputs.push(output); self } @@ -1276,7 +1276,7 @@ impl V2TransactionBuilder { // Sign all PublicKey or UnlockConditions policies with the provided keypairs // Incapable of handling threshold policies - pub fn sign_simple(mut self, keypairs: Vec<&Keypair>) -> Self { + pub fn sign_simple(&mut self, keypairs: Vec<&Keypair>) -> &mut Self { let sig_hash = self.input_sig_hash(); for keypair in keypairs { let sig = keypair.sign(&sig_hash.0); From 977f0751e5131fe53341ccb442e2288d81316f03 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:43:44 -0400 Subject: [PATCH 137/233] add ApiClientHelpers::fund_tx_single_source WIP --- src/transport/client/helpers.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 51117a9..6deaca7 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,6 +1,6 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, ConsensusTipRequest, GetAddressUtxosRequest, AddressBalanceResponse}; -use crate::types::{Address, Currency, SiacoinElement}; +use crate::types::{Address, Currency, SiacoinElement, V2TransactionBuilder, PublicKey, SpendPolicy}; use async_trait::async_trait; use thiserror::Error; @@ -75,4 +75,35 @@ pub trait ApiClientHelpers: ApiClient { Ok((selected, change.into())) } + + /// Fund a transaction with utxos from the given address. + /// Will add inputs from the given address until the total amount from outputs is reached. + /// Will add the change amount to the transaction outputs + /// Returns the difference between inputs and outputs that would be paid to the miner. + /// See `select_unspent_outputs` for more details. + /// # Arguments + /// * `tx_builder` - A mutable reference to a `V2TransactionBuilder. + /// * `public_key` - The public key of the address to spend utxos from. + /// * `miner_fee` - The amount to pay to the miner. + /// # Returns + /// * `Ok(Currency)` - The difference between inputs and outputs that would be paid to the miner. + async fn fund_tx_single_source(&self, tx_builder: &mut V2TransactionBuilder, public_key: &PublicKey, miner_fee: Currency) -> Result<(), ApiClientHelpersError> { + let address = public_key.address(); + let outputs_total : Currency = tx_builder.siacoin_outputs.iter().map(|output| output.value).sum(); + + // select utxos from public key's address that total at least the sum of outputs and miner fee + let (selected_utxos, change) = self.select_unspent_outputs(&address, outputs_total+miner_fee).await?; + // FIXME OMAR take a look + // add selected utxos as inputs to the transaction + // for utxo in &selected_utxos { + // tx_builder.add_siacoin_input(utxo.clone(), SpendPolicy::PublicKey(public_key.clone())); + // } + + // if change > Currency::DUST { + // // add change as an output + // tx_builder.add_siacoin_output((address, change).into()); + // } + + Ok(()) + } } \ No newline at end of file From 0ebf9eee9bd564f401fef3421dc5ad10c816c16b Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:51:32 -0400 Subject: [PATCH 138/233] uncomment code --- src/transport/client/helpers.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 6deaca7..40c2867 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -95,14 +95,14 @@ pub trait ApiClientHelpers: ApiClient { let (selected_utxos, change) = self.select_unspent_outputs(&address, outputs_total+miner_fee).await?; // FIXME OMAR take a look // add selected utxos as inputs to the transaction - // for utxo in &selected_utxos { - // tx_builder.add_siacoin_input(utxo.clone(), SpendPolicy::PublicKey(public_key.clone())); - // } + for utxo in &selected_utxos { + tx_builder.add_siacoin_input(utxo.clone(), SpendPolicy::PublicKey(public_key.clone())); + } - // if change > Currency::DUST { - // // add change as an output - // tx_builder.add_siacoin_output((address, change).into()); - // } + if change > Currency::DUST { + // add change as an output + tx_builder.add_siacoin_output((address, change).into()); + } Ok(()) } From 73c865582a2ed001a6cb48aa3ce9de2cb81a2251 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 06:54:59 -0400 Subject: [PATCH 139/233] cargo fmt; Cargo.lock --- Cargo.lock | 270 +---------- src/blake2b_internal.rs | 64 +-- src/encoding.rs | 4 +- src/tests/encoding.rs | 7 +- src/tests/serde.rs | 764 ++++++++++++++++---------------- src/tests/spend_policy.rs | 4 +- src/tests/transaction.rs | 14 +- src/transport/client/helpers.rs | 47 +- src/transport/endpoints.rs | 4 +- src/types.rs | 6 +- src/types/atomic_swap.rs | 317 ++++++------- src/types/keypair.rs | 25 +- src/types/signature.rs | 2 +- src/types/spend_policy.rs | 15 +- src/types/transaction.rs | 50 ++- 15 files changed, 663 insertions(+), 930 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ec2a60..976ea54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,12 +58,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - [[package]] name = "bitvec" version = "0.19.5" @@ -174,16 +168,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -316,57 +300,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.0.1" @@ -614,19 +553,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" version = "0.1.53" @@ -682,25 +608,6 @@ dependencies = [ "hashbrown 0.14.3", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - [[package]] name = "ipnet" version = "2.7.2" @@ -735,7 +642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "ryu", "static_assertions", @@ -756,12 +663,6 @@ dependencies = [ "cc", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "log" version = "0.4.19" @@ -798,23 +699,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "6.1.2" @@ -859,50 +743,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" -dependencies = [ - "bitflags 2.6.0", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "percent-encoding" version = "2.1.0" @@ -921,12 +761,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - [[package]] name = "ppv-lite86" version = "0.2.8" @@ -998,15 +832,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "reqwest" version = "0.11.9" @@ -1023,13 +848,11 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", - "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -1038,7 +861,6 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", - "tokio-native-tls", "tokio-rustls", "url", "wasm-bindgen", @@ -1069,20 +891,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustix" -version = "0.36.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - [[package]] name = "rustls" version = "0.20.4" @@ -1110,15 +918,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys 0.48.0", -] - [[package]] name = "scoped-tls" version = "1.0.0" @@ -1141,29 +940,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.189" @@ -1358,19 +1134,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tempfile" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", -] - [[package]] name = "termcolor" version = "1.2.0" @@ -1452,16 +1215,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.23.2" @@ -1592,12 +1345,6 @@ dependencies = [ "serde", ] -[[package]] -name = "vcpkg" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" - [[package]] name = "version_check" version = "0.9.4" @@ -1776,21 +1523,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index d878b89..2b3a9ff 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -155,7 +155,7 @@ fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> Hash256 { mod test { use super::*; - cross_target_tests!{ + cross_target_tests! { fn test_accumulator_new() { let default_accumulator = Accumulator::default(); @@ -167,51 +167,51 @@ mod test { } fn test_accumulator_root_default() { assert_eq!(Accumulator::default().root(), Hash256::default()) } - + fn test_accumulator_root() { let mut accumulator = Accumulator::default(); - + let timelock_leaf = timelock_leaf(0u64); accumulator.add_leaf(timelock_leaf); - + let pubkey = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); accumulator.add_leaf(pubkey_leaf); - + let sigs_required_leaf = sigs_required_leaf(1u64); accumulator.add_leaf(sigs_required_leaf); - + let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(accumulator.root(), expected); } - + fn test_accumulator_add_leaf_standard_unlock_hash() { let mut accumulator = Accumulator::default(); - + let pubkey = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - + let pubkey_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey)); let timelock_leaf = timelock_leaf(0u64); let sigs_required_leaf = sigs_required_leaf(1u64); - + accumulator.add_leaf(timelock_leaf); accumulator.add_leaf(pubkey_leaf); accumulator.add_leaf(sigs_required_leaf); - + let root = accumulator.root(); let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(root, expected) } - + fn test_accumulator_add_leaf_2of2_multisig_unlock_hash() { let mut accumulator = Accumulator::default(); - + let pubkey1 = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) @@ -220,26 +220,26 @@ mod test { &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - + let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); - + let timelock_leaf = timelock_leaf(0u64); let sigs_required_leaf = sigs_required_leaf(2u64); - + accumulator.add_leaf(timelock_leaf); accumulator.add_leaf(pubkey1_leaf); accumulator.add_leaf(pubkey2_leaf); accumulator.add_leaf(sigs_required_leaf); - + let root = accumulator.root(); let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(root, expected) } - + fn test_accumulator_add_leaf_1of2_multisig_unlock_hash() { let mut accumulator = Accumulator::default(); - + let pubkey1 = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) @@ -248,34 +248,34 @@ mod test { &hex::decode("0101010000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - + let pubkey1_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey1)); let pubkey2_leaf = public_key_leaf(&UnlockKey::Ed25519(pubkey2)); - + let timelock_leaf = timelock_leaf(0u64); let sigs_required_leaf = sigs_required_leaf(1u64); - + accumulator.add_leaf(timelock_leaf); accumulator.add_leaf(pubkey1_leaf); accumulator.add_leaf(pubkey2_leaf); accumulator.add_leaf(sigs_required_leaf); - + let root = accumulator.root(); let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(root, expected) } - + fn test_standard_unlock_hash() { let pubkey = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - + let hash = standard_unlock_hash(&pubkey); let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } - + fn test_hash_blake2b_pair() { let left: [u8; 32] = hex::decode("cdcce3978a58ceb6c8480d218646db4eae85eb9ea9c2f5138fbacb4ce2c701e3") .unwrap() @@ -285,36 +285,36 @@ mod test { .unwrap() .try_into() .unwrap(); - + let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } - + fn test_timelock_leaf() { let hash = timelock_leaf(0); let expected = Hash256(STANDARD_TIMELOCK_BLAKE2B_HASH); assert_eq!(hash, expected) } - + fn test_sigs_required_leaf() { let hash = sigs_required_leaf(1u64); let expected = Hash256(STANDARD_SIGS_REQUIRED_BLAKE2B_HASH); assert_eq!(hash, expected) } - + fn test_hash_blake2b_single() { let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } - + fn test_public_key_leaf() { let pubkey = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - + let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) diff --git a/src/encoding.rs b/src/encoding.rs index 3d97ca0..907c3e9 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -62,7 +62,7 @@ mod test { use super::*; use std::convert::TryFrom; - cross_target_tests!{ + cross_target_tests! { fn test_encoder_default_hash() { assert_eq!( Encoder::default().hash(), @@ -143,4 +143,4 @@ mod test { ); } } -} \ No newline at end of file +} diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 126e50f..4e86280 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -1,4 +1,3 @@ - #[cfg(test)] mod test { use crate::blake2b_internal::standard_unlock_hash; @@ -6,8 +5,8 @@ mod test { use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition}; use std::convert::TryFrom; use std::str::FromStr; - - cross_target_tests!{ + + cross_target_tests! { fn test_unlock_condition_unlock_hash_2of2_multisig() { let pubkey = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -172,4 +171,4 @@ mod test { assert_eq!(hash, expected); } } -} \ No newline at end of file +} diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 899f3cc..b140ab9 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,5 +1,3 @@ - - // // FIXME reminder to populate the following tests // #[test] // #[ignore] @@ -104,52 +102,67 @@ mod test { }}; } // Ensure the original value matches the value after round-trip (serialize -> deserialize -> serialize) - use crate::types::{Address, Event, Hash256, UnlockKey, SiacoinElement, SiacoinOutput, StateElement, V2Transaction}; - + use crate::types::{Address, Event, Hash256, SiacoinElement, SiacoinOutput, StateElement, UnlockKey, V2Transaction}; + cross_target_tests! { - fn test_serde_address() { - test_serde!( - Address, - json!("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") - ); - } - - fn test_serde_unlock_key() { - test_serde!( - UnlockKey, - json!("ed25519:0102030000000000000000000000000000000000000000000000000000000000") - ); - } - - fn test_serde_sia_hash() { - test_serde!( - Hash256, - json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") + fn test_serde_address() { + test_serde!( + Address, + json!("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") + ); + } + + fn test_serde_unlock_key() { + test_serde!( + UnlockKey, + json!("ed25519:0102030000000000000000000000000000000000000000000000000000000000") + ); + } + + fn test_serde_sia_hash() { + test_serde!( + Hash256, + json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") + ); + } + + fn test_serde_siacoin_output() { + let j = json!({ + "value": "300000000000000000000000000000", + "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + }); + test_serde!(SiacoinOutput, j); + } + + fn test_serde_state_element() { + let j = json!({ + "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": null + }); + serde_json::from_value::(j).unwrap(); + } + + fn test_serde_siacoin_element() { + let j = json!( { + "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": ["h:8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], + "siacoinOutput": { + "value": "300000000000000000000000000000", + "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + }, + "maturityHeight": 154 + } ); + serde_json::from_value::(j).unwrap(); } - - fn test_serde_siacoin_output() { - let j = json!({ - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - }); - test_serde!(SiacoinOutput, j); - } - - fn test_serde_state_element() { - let j = json!({ - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": null - }); - serde_json::from_value::(j).unwrap(); - } - - fn test_serde_siacoin_element() { + + fn test_serde_siacoin_element_null_merkle_proof() { let j = json!( { "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", "leafIndex": 21, - "merkleProof": ["h:8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], + "merkleProof": null, "siacoinOutput": { "value": "300000000000000000000000000000", "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" @@ -159,312 +172,355 @@ mod test { ); serde_json::from_value::(j).unwrap(); } - - fn test_serde_siacoin_element_null_merkle_proof() { - let j = json!( { - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": null, - "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" - }, - "maturityHeight": 154 - } - ); - serde_json::from_value::(j).unwrap(); -} -fn test_serde_event_v2_contract_resolution_storage_proof() { - let j = json!( - { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", - "index": { - "height": 201, - "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 345, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", - "leafIndex": 397, - "merkleProof": [ - "h:4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", - "h:e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", - "h:0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", - "h:37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", - "h:077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 200, - "expirationHeight": 210, - "renterOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "hostOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + fn test_serde_event_v2_contract_resolution_storage_proof() { + let j = json!( + { + "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "index": { + "height": 201, + "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 345, + "type": "v2ContractResolution", + "data": { + "resolution": { + "parent": { + "id": "h:b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", + "leafIndex": 397, + "merkleProof": [ + "h:4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", + "h:e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", + "h:0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", + "h:37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", + "h:077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 200, + "expirationHeight": 210, + "renterOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "hostOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d", + "hostSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d" + } + }, + "type": "storageProof", + "resolution": { + "proofIndex": { + "id": "h:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", + "leafIndex": 416, + "merkleProof": [], + "chainIndex": { + "height": 200, + "id": "bid:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290" + } }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d", - "hostSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d" + "leaf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "proof": [] } }, - "type": "storageProof", - "resolution": { - "proofIndex": { - "id": "h:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", - "leafIndex": 416, - "merkleProof": [], - "chainIndex": { - "height": 200, - "id": "bid:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290" - } + "siacoinElement": { + "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "leafIndex": 418, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "leaf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "proof": [] - } - }, - "siacoinElement": { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", - "leafIndex": 418, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "maturityHeight": 345 }, - "maturityHeight": 345 - }, - "missed": false + "missed": false + } } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); - - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde -} + ); -fn test_serde_event_v2_contract_resolution_renewal() { - let j = json!( - { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", - "index": { - "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 347, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", - "leafIndex": 423, - "merkleProof": [ - "h:ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", - "h:147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", - "h:1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + let _event = serde_json::from_value::(j).unwrap(); + + // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde + } + + fn test_serde_event_v2_contract_resolution_renewal() { + let j = json!( + { + "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "index": { + "height": 203, + "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 347, + "type": "v2ContractResolution", + "data": { + "resolution": { + "parent": { + "id": "h:06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", + "leafIndex": 423, + "merkleProof": [ + "h:ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", + "h:147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", + "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", + "h:1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03", + "hostSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03" + } + }, + "type": "renewal", + "resolution": { + "finalRevision": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 18446744073709551615u64, + "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "newContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 221, + "expirationHeight": 231, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03", - "hostSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03" + "renterRollover": "0", + "hostRollover": "0", + "renterSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08", + "hostSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08" } }, - "type": "renewal", + "siacoinElement": { + "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "leafIndex": 427, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 347 + }, + "missed": false + } + } + ); + + let _event = serde_json::from_value::(j).unwrap(); + + // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde + } + + #[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) + fn test_serde_event_v2_contract_resolution_expiration() { + let j = json!( + { + "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "index": { + "height": 190, + "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 334, + "type": "v2ContractResolution", + "data": { "resolution": { - "finalRevision": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 18446744073709551615u64, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "parent": { + "id": "h:34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", + "leafIndex": 351, + "merkleProof": [ + "h:e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", + "h:70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", + "h:42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", + "h:4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", + "h:59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", + "h:f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", + "h:3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" + ], + "v2FileContract": { + "filesize": 0, + "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 179, + "expirationHeight": 189, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", + "hostSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" + } }, - "newContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 221, - "expirationHeight": 231, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "type": "expiration", + "resolution": {} + }, + "siacoinElement": { + "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "leafIndex": 391, + "merkleProof": null, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "renterRollover": "0", - "hostRollover": "0", - "renterSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08", - "hostSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08" - } - }, - "siacoinElement": { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", - "leafIndex": 427, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "maturityHeight": 334 }, - "maturityHeight": 347 - }, - "missed": false + "missed": true + } } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); - - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde -} + ); -#[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) -fn test_serde_event_v2_contract_resolution_expiration() { - let j = json!( - { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", - "index": { - "height": 190, - "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 334, - "type": "v2ContractResolution", - "data": { - "resolution": { - "parent": { - "id": "h:34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", - "leafIndex": 351, - "merkleProof": [ - "h:e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", - "h:70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", - "h:42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", - "h:4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", - "h:59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", - "h:f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", - "h:3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 179, - "expirationHeight": 189, - "renterOutput": { - "value": "10000000000000000000000000000", + let _event = serde_json::from_value::(j).unwrap(); + } + + fn test_serde_event_v2_transaction() { + let j = json!( + { + "id": "h:5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", + "index": { + "height": 203, + "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + }, + "timestamp": "2024-07-18T19:04:16Z", + "maturityHeight": 203, + "type": "v2Transaction", + "data": { + "siacoinInputs": [ + { + "parent": { + "id": "h:78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", + "leafIndex": 421, + "merkleProof": [ + "h:f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", + "h:d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", + "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" + ], + "siacoinOutput": { + "value": "256394172736732570239334030000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "hostOutput": { - "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "maturityHeight": 0 + }, + "satisfiedPolicy": { + "policy": { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" + ], + "signaturesRequired": 1 + } }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", - "hostSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" + "signatures": [ + "sig:c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" + ] } - }, - "type": "expiration", - "resolution": {} - }, - "siacoinElement": { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", - "leafIndex": 391, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", + } + ], + "siacoinOutputs": [ + { + "value": "10400000000000000000000000000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "maturityHeight": 334 - }, - "missed": true + { + "value": "245994172736732570239334030000", + "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + } + ], + "minerFee": "0" + } } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); -} + ); + test_serde!(Event, j); + } -fn test_serde_event_v2_transaction() { - let j = json!( - { - "id": "h:5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", - "index": { - "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" - }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 203, - "type": "v2Transaction", - "data": { + fn test_v2_transaction_serde_basic_send() { + let j = json!( + { "siacoinInputs": [ { "parent": { - "id": "h:78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", - "leafIndex": 421, + "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", + "leafIndex": 3, "merkleProof": [ - "h:f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", - "h:d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" + "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" ], "siacoinOutput": { - "value": "256394172736732570239334030000", + "value": "300000000000000000000000000000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "maturityHeight": 0 + "maturityHeight": 145 }, "satisfiedPolicy": { "policy": { @@ -478,87 +534,29 @@ fn test_serde_event_v2_transaction() { } }, "signatures": [ - "sig:c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" + "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" ] } } ], "siacoinOutputs": [ { - "value": "10400000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "1000000000000000000000000000", + "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, { - "value": "245994172736732570239334030000", + "value": "299000000000000000000000000000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } ], "minerFee": "0" } - } - ); - test_serde!(Event, j); -} + ); + let tx = serde_json::from_value::(j).unwrap(); -fn test_v2_transaction_serde_basic_send() { - let j = json!( - { - "siacoinInputs": [ - { - "parent": { - "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", - "leafIndex": 3, - "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" - ], - "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 145 - }, - "satisfiedPolicy": { - "policy": { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" - ], - "signaturesRequired": 1 - } - }, - "signatures": [ - "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" - ] - } - } - ], - "siacoinOutputs": [ - { - "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - { - "value": "299000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - } - ], - "minerFee": "0" - } - ); - let tx = serde_json::from_value::(j).unwrap(); - - let j2 = serde_json::to_value(&tx).unwrap().to_string(); - let tx2 = serde_json::from_str::(&j2).unwrap(); - assert_eq!(tx, tx2); -} + let j2 = serde_json::to_value(&tx).unwrap().to_string(); + let tx2 = serde_json::from_str::(&j2).unwrap(); + assert_eq!(tx, tx2); + } + } } -} \ No newline at end of file diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 5d658bc..d44866c 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,5 +1,3 @@ - - #[cfg(test)] mod test { use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition, UnlockKey}; @@ -161,4 +159,4 @@ mod test { assert_eq!(spend_policy, spend_policy_deser); } } -} \ No newline at end of file +} diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 45829ce..8cc0f1d 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -1,14 +1,14 @@ #[cfg(test)] mod test { use crate::encoding::Encoder; - use crate::types::{Address, Hash256, Keypair, PublicKey, Signature, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, SatisfiedPolicy, - SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputVersion, - StateElement, V2FileContract, V2FileContractElement, V2Transaction, SpendPolicy, - UnlockCondition}; + use crate::types::{Address, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, Hash256, Keypair, + PublicKey, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, + SiacoinOutputVersion, Signature, SpendPolicy, StateElement, UnlockCondition, V2FileContract, + V2FileContractElement, V2Transaction}; use std::convert::TryFrom; use std::str::FromStr; - - cross_target_tests!{ + + cross_target_tests! { fn test_siacoin_input_encode() { let public_key = PublicKey::from_bytes( &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -711,4 +711,4 @@ mod test { assert_eq!(tx.siacoin_inputs[0].satisfied_policy.signatures[0], sig); } } -} \ No newline at end of file +} diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 40c2867..37ae54c 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,16 +1,16 @@ use super::{ApiClient, ApiClientError}; -use crate::transport::endpoints::{AddressBalanceRequest, ConsensusTipRequest, GetAddressUtxosRequest, AddressBalanceResponse}; -use crate::types::{Address, Currency, SiacoinElement, V2TransactionBuilder, PublicKey, SpendPolicy}; +use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, + GetAddressUtxosRequest}; +use crate::types::{Address, Currency, PublicKey, SiacoinElement, SpendPolicy, V2TransactionBuilder}; use async_trait::async_trait; use thiserror::Error; #[derive(Debug, Error)] pub enum ApiClientHelpersError { - #[error("ApiClientHelpersError::SelectOutputs: insufficent amount, available: {available:?} required: {required:?}")] - SelectOutputs { - available: Currency, - required: Currency, - }, + #[error( + "ApiClientHelpersError::SelectOutputs: insufficent amount, available: {available:?} required: {required:?}" + )] + SelectOutputs { available: Currency, required: Currency }, #[error("ApiClientHelpersError::ApiClientError: {0}")] ApiClientError(#[from] ApiClientError), } @@ -28,8 +28,18 @@ pub trait ApiClientHelpers: ApiClient { self.dispatcher(AddressBalanceRequest { address }).await } - async fn get_unspent_outputs(&self, address: &Address, limit: Option, offset: Option) -> Result, ApiClientError> { - self.dispatcher(GetAddressUtxosRequest { address: address.clone(), limit, offset }).await + async fn get_unspent_outputs( + &self, + address: &Address, + limit: Option, + offset: Option, + ) -> Result, ApiClientError> { + self.dispatcher(GetAddressUtxosRequest { + address: address.clone(), + limit, + offset, + }) + .await } /// Fetches unspent outputs for the given address and attempts to select a subset of outputs @@ -46,7 +56,11 @@ pub trait ApiClientHelpers: ApiClient { /// This function returns `Result<(Vec, Currency), ApiClientHelpersError>`: /// * `Ok((Vec, Currency))` - A tuple containing, a vector of the selected unspent outputs and the change amount. /// * `Err(MmError)` - An error is returned if the available outputs cannot meet the required amount or a transport error is encountered. - async fn select_unspent_outputs(&self, address: &Address, total_amount: Currency) -> Result<(Vec, Currency), ApiClientHelpersError> { + async fn select_unspent_outputs( + &self, + address: &Address, + total_amount: Currency, + ) -> Result<(Vec, Currency), ApiClientHelpersError> { let mut unspent_outputs = self.get_unspent_outputs(address, None, None).await?; // Sort outputs from largest to smallest @@ -87,12 +101,17 @@ pub trait ApiClientHelpers: ApiClient { /// * `miner_fee` - The amount to pay to the miner. /// # Returns /// * `Ok(Currency)` - The difference between inputs and outputs that would be paid to the miner. - async fn fund_tx_single_source(&self, tx_builder: &mut V2TransactionBuilder, public_key: &PublicKey, miner_fee: Currency) -> Result<(), ApiClientHelpersError> { + async fn fund_tx_single_source( + &self, + tx_builder: &mut V2TransactionBuilder, + public_key: &PublicKey, + miner_fee: Currency, + ) -> Result<(), ApiClientHelpersError> { let address = public_key.address(); - let outputs_total : Currency = tx_builder.siacoin_outputs.iter().map(|output| output.value).sum(); + let outputs_total: Currency = tx_builder.siacoin_outputs.iter().map(|output| output.value).sum(); // select utxos from public key's address that total at least the sum of outputs and miner fee - let (selected_utxos, change) = self.select_unspent_outputs(&address, outputs_total+miner_fee).await?; + let (selected_utxos, change) = self.select_unspent_outputs(&address, outputs_total + miner_fee).await?; // FIXME OMAR take a look // add selected utxos as inputs to the transaction for utxo in &selected_utxos { @@ -106,4 +125,4 @@ pub trait ApiClientHelpers: ApiClient { Ok(()) } -} \ No newline at end of file +} diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index a6df5ab..d55104b 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,5 +1,5 @@ use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::types::{Address, BlockID, Event, Hash256, Currency, SiacoinElement, V1Transaction, V2Transaction}; +use crate::types::{Address, BlockID, Currency, Event, Hash256, SiacoinElement, V1Transaction, V2Transaction}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -423,4 +423,4 @@ impl SiaApiRequest for DebugMineRequest { .build(), ) } -} \ No newline at end of file +} diff --git a/src/types.rs b/src/types.rs index 30b4db4..6d389b5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -17,7 +17,7 @@ mod signature; pub use signature::{Signature, SignatureError}; mod keypair; -pub use keypair::{Keypair, PublicKeyError, PublicKey, PrivateKeyError}; +pub use keypair::{Keypair, PrivateKeyError, PublicKey, PublicKeyError}; mod spend_policy; pub use spend_policy::*; @@ -87,9 +87,7 @@ impl Address { Address(hash) } - pub fn from_public_key(pubkey: &PublicKey) -> Self { - SpendPolicy::PublicKey(pubkey.clone()).address() - } + pub fn from_public_key(pubkey: &PublicKey) -> Self { SpendPolicy::PublicKey(pubkey.clone()).address() } } impl Encodable for Address { diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs index 06d0603..7fdf13d 100644 --- a/src/types/atomic_swap.rs +++ b/src/types/atomic_swap.rs @@ -3,22 +3,22 @@ use thiserror::Error; /* The full representation of the atomic swap contract is as follows: - SpendPolicy::Threshold { + SpendPolicy::Threshold { n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, + of: vec![ + SpendPolicy::Threshold { + n: 2, of: vec![ SpendPolicy::Hash(), SpendPolicy::PublicKey() - ] + ] }, - SpendPolicy::Threshold { - n: 2, + SpendPolicy::Threshold { + n: 2, of: vec![ SpendPolicy::After(), SpendPolicy::PublicKey() - ] + ] }, ] } @@ -42,15 +42,15 @@ Alice can spend the UTXO by providing a signature, the secret and revealing the Alice can construct the following SatisfiedPolicy to spend the UTXO: SatisfiedPolicy { - policy: SpendPolicy::Threshold { + policy: SpendPolicy::Threshold { n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, + of: vec![ + SpendPolicy::Threshold { + n: 2, of: vec![ SpendPolicy::Hash(), SpendPolicy::PublicKey() - ] + ] }, SpendPolicy::Opaque(), ] @@ -62,15 +62,15 @@ SatisfiedPolicy { Similarly, Bob can spend the UTXO with the following SatisfiedPolicy assuming he waits until the timestamp has passed: SatisfiedPolicy { - policy: SpendPolicy::Threshold { + policy: SpendPolicy::Threshold { n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, + of: vec![ + SpendPolicy::Threshold { + n: 2, of: vec![ SpendPolicy::After(), SpendPolicy::PublicKey() - ] + ] }, SpendPolicy::Opaque(), ] @@ -103,9 +103,9 @@ pub enum AtomicSwapError { #[error("invalid atomic swap, invalid time component: {}", .0)] InvalidTimeComponent(ComponentError), #[error("invalid atomic swap, wrong n:{} policy: {:?}", n, policy)] - InvalidN{ policy: SpendPolicy, n : u8 }, + InvalidN { policy: SpendPolicy, n: u8 }, #[error("invalid atomic swap, wrong m:{} policy: {:?}", m, policy)] - InvalidM{ policy: SpendPolicy, m : usize }, + InvalidM { policy: SpendPolicy, m: usize }, #[error("invalid atomic swap, not a threshold: {:?}", .0)] InvalidSpendPolicyVariant(SpendPolicy), } @@ -119,39 +119,34 @@ pub enum AtomicSwapError { pub struct AtomicSwap(SpendPolicy); impl AtomicSwap { - pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self(policy)) - } + pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } - pub fn address(&self) -> Address { - self.inner().address() - } + pub fn address(&self) -> Address { self.inner().address() } - pub fn opacify(&self) -> SpendPolicy { - self.inner().opacify() - } + pub fn opacify(&self) -> SpendPolicy { self.inner().opacify() } } impl IsValidatedPolicy for AtomicSwap { type Error = AtomicSwapError; type Inner = SpendPolicy; - fn inner(&self) -> &Self::Inner { - &self.0 - } + fn inner(&self) -> &Self::Inner { &self.0 } fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { - SpendPolicy::Threshold { - n: 1, - of - } if of.len() == 2 => { + SpendPolicy::Threshold { n: 1, of } if of.len() == 2 => { HashLockPath::is_valid(&of[0]).map_err(AtomicSwapError::InvalidHashComponent)?; TimeLockPath::is_valid(&of[1]).map_err(AtomicSwapError::InvalidTimeComponent)?; Ok(()) }, - SpendPolicy::Threshold { n: 1, of } => Err(AtomicSwapError::InvalidM{ policy: policy.clone(), m: of.len() }), - SpendPolicy::Threshold { n, of: _ } => Err(AtomicSwapError::InvalidN{ policy: policy.clone(), n: n.clone() }), + SpendPolicy::Threshold { n: 1, of } => Err(AtomicSwapError::InvalidM { + policy: policy.clone(), + m: of.len(), + }), + SpendPolicy::Threshold { n, of: _ } => Err(AtomicSwapError::InvalidN { + policy: policy.clone(), + n: n.clone(), + }), _ => Err(AtomicSwapError::InvalidSpendPolicyVariant(policy.clone())), } } @@ -164,29 +159,29 @@ pub enum ComponentError { #[error("invalid hash lock component, not a threshold: {:?}", .0)] HashLockInvalidSpendPolicyVariant(SpendPolicy), #[error("invalid hash lock component, wrong n:{} policy: {:?}", n, policy)] - HashLockInvalidN{ policy: SpendPolicy, n : u8 }, + HashLockInvalidN { policy: SpendPolicy, n: u8 }, #[error("invalid hash lock component, wrong m:{} policy: {:?}", m, policy)] - HashLockInvalidM{ policy: SpendPolicy, m : usize }, + HashLockInvalidM { policy: SpendPolicy, m: usize }, #[error("invalid time lock component, time lock path: {:?}", .0)] TimeLockInvalidThresholdStructure(SpendPolicy), #[error("invalid time lock component, not a threshold: {:?}", .0)] TimeLockInvalidSpendPolicyVariant(SpendPolicy), #[error("invalid time lock component, wrong n:{} policy: {:?}", n, policy)] - TimeLockInvalidN{ policy: SpendPolicy, n : u8 }, + TimeLockInvalidN { policy: SpendPolicy, n: u8 }, #[error("invalid time lock component, wrong m:{} policy: {:?}", m, policy)] - TimeLockInvalidM{ policy: SpendPolicy, m : usize }, + TimeLockInvalidM { policy: SpendPolicy, m: usize }, #[error("invalid component, not SpendPolicy::Opaque: {:?}", .0)] OpaqueInvalidSpendPolicyVariant(SpendPolicy), } /// The hash locked threshold path of the atomic swap contract. /// structure: -/// SpendPolicy::Threshold { -/// n: 2, +/// SpendPolicy::Threshold { +/// n: 2, /// of: vec![ /// SpendPolicy::Hash(sha256(secret)), /// SpendPolicy::PublicKey(public_key) -/// ] +/// ] /// } /// fulfillment conditions: /// - signature from participant's public key @@ -196,30 +191,29 @@ pub enum ComponentError { pub struct HashLockPath(SpendPolicy); impl HashLockPath { - pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self(policy)) - } - + pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } } impl IsValidatedPolicy for HashLockPath { type Error = ComponentError; type Inner = SpendPolicy; - fn inner(&self) -> &Self::Inner { - &self.0 - } + fn inner(&self) -> &Self::Inner { &self.0 } fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { - SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { - match of.as_slice() { - [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] => Ok(()), - _ => Err(ComponentError::HashLockInvalidThresholdStructure(policy.clone())), - } + SpendPolicy::Threshold { n: 2, of } if of.len() == 2 => match of.as_slice() { + [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] => Ok(()), + _ => Err(ComponentError::HashLockInvalidThresholdStructure(policy.clone())), }, - SpendPolicy::Threshold{ n: 2, of } => Err(ComponentError::HashLockInvalidM{ policy: policy.clone(), m: of.len() }), - SpendPolicy::Threshold{ n, of: _ } => Err(ComponentError::HashLockInvalidN{ policy: policy.clone(), n: n.clone() }), + SpendPolicy::Threshold { n: 2, of } => Err(ComponentError::HashLockInvalidM { + policy: policy.clone(), + m: of.len(), + }), + SpendPolicy::Threshold { n, of: _ } => Err(ComponentError::HashLockInvalidN { + policy: policy.clone(), + n: n.clone(), + }), _ => Err(ComponentError::HashLockInvalidSpendPolicyVariant(policy.clone())), } } @@ -227,12 +221,12 @@ impl IsValidatedPolicy for HashLockPath { /// The time based threshold path of the atomic swap contract. /// structure: -/// SpendPolicy::Threshold { -/// n: 2, +/// SpendPolicy::Threshold { +/// n: 2, /// of: vec![ /// SpendPolicy::After(timestamp), /// SpendPolicy::PublicKey(public_key) -/// ] +/// ] /// } /// fulfillment conditions: /// - signature from participant's public key @@ -241,29 +235,29 @@ impl IsValidatedPolicy for HashLockPath { pub struct TimeLockPath(SpendPolicy); impl TimeLockPath { - pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self(policy)) - } + pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } } impl IsValidatedPolicy for TimeLockPath { type Error = ComponentError; type Inner = SpendPolicy; - fn inner(&self) -> &Self::Inner { - &self.0 - } + fn inner(&self) -> &Self::Inner { &self.0 } fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { - SpendPolicy::Threshold{ n: 2, of } if of.len() == 2 => { - match of.as_slice() { - [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] => Ok(()), - _ => Err(ComponentError::TimeLockInvalidThresholdStructure(policy.clone())), - } + SpendPolicy::Threshold { n: 2, of } if of.len() == 2 => match of.as_slice() { + [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] => Ok(()), + _ => Err(ComponentError::TimeLockInvalidThresholdStructure(policy.clone())), }, - SpendPolicy::Threshold{ n: 2, of } => Err(ComponentError::TimeLockInvalidM{ policy: policy.clone(), m: of.len() }), - SpendPolicy::Threshold{ n, of: _ } => Err(ComponentError::TimeLockInvalidN{ policy: policy.clone(), n: n.clone() }), + SpendPolicy::Threshold { n: 2, of } => Err(ComponentError::TimeLockInvalidM { + policy: policy.clone(), + m: of.len(), + }), + SpendPolicy::Threshold { n, of: _ } => Err(ComponentError::TimeLockInvalidN { + policy: policy.clone(), + n: n.clone(), + }), _ => Err(ComponentError::TimeLockInvalidSpendPolicyVariant(policy.clone())), } } @@ -273,18 +267,14 @@ impl IsValidatedPolicy for TimeLockPath { pub struct Opaque(SpendPolicy); impl Opaque { - pub fn new(policy: SpendPolicy) -> Result { - Self::is_valid(&policy).map(|_| Self(policy)) - } + pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } } impl IsValidatedPolicy for Opaque { type Error = ComponentError; type Inner = SpendPolicy; - fn inner(&self) -> &Self::Inner { - &self.0 - } + fn inner(&self) -> &Self::Inner { &self.0 } fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { match policy { @@ -298,15 +288,15 @@ impl IsValidatedPolicy for Opaque { /// Used within the input that spends the locked UTXO. /// structure: /// SatisfiedPolicy { -/// policy: SpendPolicy::Threshold { +/// policy: SpendPolicy::Threshold { /// n: 1, -/// of: vec![ -/// SpendPolicy::Threshold { -/// n: 2, +/// of: vec![ +/// SpendPolicy::Threshold { +/// n: 2, /// of: vec![ /// SpendPolicy::Hash(sha256(secret)), /// SpendPolicy::PublicKey(public_key) -/// ] +/// ] /// }, /// SpendPolicy::Opaque(), /// ] @@ -319,10 +309,24 @@ pub struct AtomicSwapSuccess(SatisfiedPolicy); #[derive(Debug, Error)] enum AtomicSwapSuccessError { - #[error("invalid atomic swap success: {:?}, invalid signatures amount: {}", satisfied_policy, amount)] - InvalidSignaturesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, - #[error("invalid atomic swap success: {:?}, invalid preimages amount: {}", satisfied_policy, amount)] - InvalidPreimagesAmount { satisfied_policy: SatisfiedPolicy, amount: usize }, + #[error( + "invalid atomic swap success: {:?}, invalid signatures amount: {}", + satisfied_policy, + amount + )] + InvalidSignaturesAmount { + satisfied_policy: SatisfiedPolicy, + amount: usize, + }, + #[error( + "invalid atomic swap success: {:?}, invalid preimages amount: {}", + satisfied_policy, + amount + )] + InvalidPreimagesAmount { + satisfied_policy: SatisfiedPolicy, + amount: usize, + }, #[error("invalid atomic swap success: {:?}, invalid policy variant", satisfied_policy)] InvalidSpendPolicyVariant { satisfied_policy: SatisfiedPolicy }, // #[error("invalid atomic swap success: {0}, atomic swap error")] @@ -335,9 +339,7 @@ impl IsValidatedPolicy for AtomicSwapSuccess { type Error = AtomicSwapSuccessError; type Inner = SatisfiedPolicy; - fn inner(&self) -> &Self::Inner { - &self.0 - } + fn inner(&self) -> &Self::Inner { &self.0 } fn is_valid(satisfied_policy: &Self::Inner) -> Result<(), Self::Error> { if satisfied_policy.signatures.len() != 1 { @@ -355,12 +357,14 @@ impl IsValidatedPolicy for AtomicSwapSuccess { } match &satisfied_policy.policy { - SpendPolicy::Threshold{ n: 1, of } if of.len() == 2 => { + SpendPolicy::Threshold { n: 1, of } if of.len() == 2 => { HashLockPath::is_valid(&of[0]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; Opaque::is_valid(&of[1]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; Ok(()) }, - _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant{ satisfied_policy: satisfied_policy.clone()}), + _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant { + satisfied_policy: satisfied_policy.clone(), + }), } } } @@ -371,16 +375,13 @@ mod test { use crate::types::{Hash256, PublicKey}; fn pubkey0() -> PublicKey { - PublicKey::from_bytes( - &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - ).unwrap() + PublicKey::from_bytes(&hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap()) + .unwrap() } fn pubkey1() -> PublicKey { - PublicKey::from_bytes( - &hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap(), - ) - .unwrap() + PublicKey::from_bytes(&hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap()) + .unwrap() } fn valid_atomic_swap_spend_policy() -> SpendPolicy { @@ -389,46 +390,32 @@ mod test { of: vec![ SpendPolicy::Threshold { n: 2, - of: vec![ - SpendPolicy::Hash(Hash256::default()), - SpendPolicy::PublicKey(pubkey0()), - ], + of: vec![SpendPolicy::Hash(Hash256::default()), SpendPolicy::PublicKey(pubkey0())], }, SpendPolicy::Threshold { n: 2, - of: vec![ - SpendPolicy::After(0), - SpendPolicy::PublicKey(pubkey1()), - ], + of: vec![SpendPolicy::After(0), SpendPolicy::PublicKey(pubkey1())], }, ], } } - + fn valid_component_hash_lock() -> SpendPolicy { SpendPolicy::Threshold { n: 2, - of: vec![ - SpendPolicy::Hash(Hash256::default()), - SpendPolicy::PublicKey(pubkey0()), - ], + of: vec![SpendPolicy::Hash(Hash256::default()), SpendPolicy::PublicKey(pubkey0())], } } fn valid_component_time_lock() -> SpendPolicy { SpendPolicy::Threshold { n: 2, - of: vec![ - SpendPolicy::After(0), - SpendPolicy::PublicKey(pubkey1()), - ], + of: vec![SpendPolicy::After(0), SpendPolicy::PublicKey(pubkey1())], } } #[test] - fn test_atomic_swap_contract_valid() { - AtomicSwap::new(valid_atomic_swap_spend_policy()).unwrap(); - } + fn test_atomic_swap_contract_valid() { AtomicSwap::new(valid_atomic_swap_spend_policy()).unwrap(); } #[test] fn test_atomic_swap_contract_invalid_hash_lock_path() { @@ -473,7 +460,7 @@ mod test { fn test_atomic_swap_contract_invalid_components_too_many() { let mut policy = valid_atomic_swap_spend_policy(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { of.push(SpendPolicy::PublicKey(pubkey0())); }, _ => unreachable!(), @@ -482,7 +469,7 @@ mod test { match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 3); - } + }, _ => panic!(), } } @@ -491,7 +478,7 @@ mod test { fn test_atomic_swap_contract_invalid_components_missing_time_lock_path() { let mut policy = valid_atomic_swap_spend_policy(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { let _ = of.pop().unwrap(); }, _ => unreachable!(), @@ -499,7 +486,7 @@ mod test { match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 1); - } + }, _ => panic!(), } } @@ -508,7 +495,7 @@ mod test { fn test_atomic_swap_contract_invalid_components_missing_hash_lock_path() { let mut policy = valid_atomic_swap_spend_policy(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { let _ = of.remove(0); }, _ => unreachable!(), @@ -516,7 +503,7 @@ mod test { match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 1); - } + }, _ => panic!(), } } @@ -525,7 +512,7 @@ mod test { fn test_atomic_swap_contract_invalid_components_missing_both_paths() { let mut policy = valid_atomic_swap_spend_policy(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { *of = vec![]; }, _ => unreachable!(), @@ -533,7 +520,7 @@ mod test { match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidM { policy: _, m }) => { assert_eq!(m, 0); - } + }, _ => panic!(), } } @@ -549,7 +536,7 @@ mod test { match AtomicSwap::new(policy.clone()) { Err(AtomicSwapError::InvalidN { policy: _, n }) => { assert_eq!(n, 10); - } + }, _ => panic!(), } } @@ -565,15 +552,13 @@ mod test { } #[test] - fn test_atomic_swap_component_hash_lock_valid() { - HashLockPath::new(valid_component_hash_lock()).unwrap(); - } + fn test_atomic_swap_component_hash_lock_valid() { HashLockPath::new(valid_component_hash_lock()).unwrap(); } #[test] fn test_atomic_swap_component_hash_lock_invalid_threshold_structure() { let policy = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], + of: vec![SpendPolicy::PublicKey(pubkey0()), SpendPolicy::PublicKey(pubkey0())], }; match HashLockPath::new(policy.clone()).unwrap_err() { @@ -586,7 +571,7 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_wrong_order() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { of.reverse(); }, _ => unreachable!(), @@ -602,14 +587,14 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_too_many() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { of.push(SpendPolicy::PublicKey(pubkey0())); }, _ => unreachable!(), } match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 3), + ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 3), _ => panic!(), } } @@ -618,14 +603,12 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_missing_public_key() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![SpendPolicy::Hash(Hash256::default())] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::Hash(Hash256::default())], _ => unreachable!(), } match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 1), _ => panic!(), } } @@ -634,14 +617,12 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_missing_hash() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![SpendPolicy::PublicKey(pubkey0())] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::PublicKey(pubkey0())], _ => unreachable!(), } match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 1), _ => panic!(), } } @@ -650,14 +631,12 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_empty_threshold() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![], _ => unreachable!(), } match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM{ policy: _, m } => assert_eq!(m, 0), + ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 0), _ => panic!(), } } @@ -666,14 +645,14 @@ mod test { fn test_atomic_swap_component_hash_lock_invalid_n() { let mut policy = valid_component_hash_lock(); match &mut policy { - SpendPolicy::Threshold { n, of:_ } => { + SpendPolicy::Threshold { n, of: _ } => { *n = 10; }, _ => unreachable!(), } match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidN{ policy: _, n } => assert_eq!(n, 10), + ComponentError::HashLockInvalidN { policy: _, n } => assert_eq!(n, 10), _ => panic!(), } } @@ -689,15 +668,13 @@ mod test { } #[test] - fn test_atomic_swap_component_time_lock_valid() { - TimeLockPath::new(valid_component_time_lock()).unwrap(); - } + fn test_atomic_swap_component_time_lock_valid() { TimeLockPath::new(valid_component_time_lock()).unwrap(); } #[test] fn test_atomic_swap_component_time_lock_invalid_threshold_structure() { let policy = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(pubkey0()) , SpendPolicy::PublicKey(pubkey0())], + of: vec![SpendPolicy::PublicKey(pubkey0()), SpendPolicy::PublicKey(pubkey0())], }; match TimeLockPath::new(policy.clone()).unwrap_err() { @@ -710,7 +687,7 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_wrong_order() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { of.reverse(); }, _ => unreachable!(), @@ -726,14 +703,14 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_too_many() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { + SpendPolicy::Threshold { n: _, of } => { of.push(SpendPolicy::PublicKey(pubkey0())); }, _ => unreachable!(), } match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 3), + ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 3), _ => panic!(), } } @@ -742,14 +719,12 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_missing_public_key() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![SpendPolicy::After(0)] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::After(0)], _ => unreachable!(), } match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 1), _ => panic!(), } } @@ -758,14 +733,12 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_missing_time() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![SpendPolicy::PublicKey(pubkey1())] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::PublicKey(pubkey1())], _ => unreachable!(), } match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 1), + ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 1), _ => panic!(), } } @@ -774,14 +747,12 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_empty_threshold() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n:_, of } => { - *of = vec![] - }, + SpendPolicy::Threshold { n: _, of } => *of = vec![], _ => unreachable!(), } match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM{ policy: _, m } => assert_eq!(m, 0), + ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 0), _ => panic!(), } } @@ -790,14 +761,14 @@ mod test { fn test_atomic_swap_component_time_lock_invalid_n() { let mut policy = valid_component_time_lock(); match &mut policy { - SpendPolicy::Threshold { n, of:_ } => { + SpendPolicy::Threshold { n, of: _ } => { *n = 10; }, _ => unreachable!(), } match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidN{ policy: _, n } => assert_eq!(n, 10), + ComponentError::TimeLockInvalidN { policy: _, n } => assert_eq!(n, 10), _ => panic!(), } } diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 02443c3..0789e4e 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -6,7 +6,7 @@ use std::fmt; use std::str::FromStr; use thiserror::Error; -use crate::types::{Address, SpendPolicy, Signature, SignatureError}; +use crate::types::{Address, Signature, SignatureError, SpendPolicy}; #[derive(Debug, Error)] pub enum PublicKeyError { @@ -29,7 +29,7 @@ pub enum PrivateKeyError { /// The purpose of this wrapper type is to limit the functionality of underlying ed25519 types. /// The inner fields are not public by design. /// We must not allow the consumer to create an invalid ed25519 Keypair or edit the PublicKey after creation. -/// see https://github.com/advisories/GHSA-w5vr-6qhr-36cc +/// see https://github.com/advisories/GHSA-w5vr-6qhr-36cc pub struct Keypair { public: PublicKey, private: PrivateKey, @@ -53,18 +53,14 @@ impl Keypair { pub fn sign(&self, message: &[u8]) -> Signature { Signer::sign(self, message) } - /// Verify a signature of a message with this keypair's public key. + /// Verify a signature of a message with this keypair's public key. pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { self.public.verify(message, signature) } - pub fn public(&self) -> PublicKey { - self.public.clone() - } + pub fn public(&self) -> PublicKey { self.public.clone() } - pub fn private(&self) -> [u8; SECRET_KEY_LENGTH] { - self.private.0.to_bytes() - } + pub fn private(&self) -> [u8; SECRET_KEY_LENGTH] { self.private.0.to_bytes() } } struct PrivateKey(SecretKey); @@ -106,8 +102,7 @@ impl PublicKey { // Method for parsing a hex string without the "ed25519:" prefix pub fn from_str_no_prefix(hex_str: &str) -> Result { let mut bytes = [0u8; 32]; - hex::decode_to_slice(hex_str, &mut bytes) - .map_err(|_| PublicKeyError::InvalidHex(hex_str.to_string()))?; + hex::decode_to_slice(hex_str, &mut bytes).map_err(|_| PublicKeyError::InvalidHex(hex_str.to_string()))?; let public_key = Self::from_bytes(&bytes)?; @@ -118,14 +113,10 @@ impl PublicKey { } /// Generate the default v1 address from the public key - pub fn v1_address(&self) -> Address { - SpendPolicy::unlock_condition(vec![self.clone()], 0, 1).address() - } + pub fn v1_address(&self) -> Address { SpendPolicy::unlock_condition(vec![self.clone()], 0, 1).address() } /// Generate the default v2 address from the public key - pub fn address(&self) -> Address { - SpendPolicy::PublicKey(self.clone()).address() - } + pub fn address(&self) -> Address { SpendPolicy::PublicKey(self.clone()).address() } pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { Verifier::verify(self, message, signature).map_err(SignatureError::VerifyFailed) diff --git a/src/types/signature.rs b/src/types/signature.rs index 9e1811c..3e6bf35 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -2,7 +2,7 @@ use crate::types::keypair::PublicKey; use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::ed25519::signature::{Error as SignatureCrateError, Signature as SignatureTrait}; -use ed25519_dalek::{Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, SIGNATURE_LENGTH}; +use ed25519_dalek::{Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, SIGNATURE_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 9c55a66..2e315f1 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -1,6 +1,6 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, Accumulator}; use crate::encoding::{Encodable, Encoder}; -use crate::types::{Address, Hash256, PublicKey, Signature, Specifier, Preimage, SatisfiedPolicy}; +use crate::types::{Address, Hash256, Preimage, PublicKey, SatisfiedPolicy, Signature, Specifier}; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; use nom::combinator::all_consuming; @@ -184,29 +184,24 @@ impl SpendPolicy { pub fn atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { let policy_after = SpendPolicy::After(lock_time); let policy_hash = SpendPolicy::Hash(hash); - + let policy_success = SpendPolicy::Threshold { n: 2, of: vec![SpendPolicy::PublicKey(alice), policy_hash], }; - + let policy_refund = SpendPolicy::Threshold { n: 2, of: vec![SpendPolicy::PublicKey(bob), policy_after], }; - + SpendPolicy::Threshold { n: 1, of: vec![policy_success, policy_refund], } } - pub fn atomic_swap_success( - alice: PublicKey, - bob: PublicKey, - lock_time: u64, - hash: Hash256, - ) -> Self { + pub fn atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { match Self::atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[1] = of[1].opacify(); diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 7faf4e8..e197413 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1,7 +1,8 @@ use crate::encoding::{Encodable, Encoder}; -use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature, SpendPolicy, UnlockCondition, UnlockKey}; +use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature, SpendPolicy, UnlockCondition, + UnlockKey}; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use derive_more::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign, Display, From, Into, Sum, Deref}; +use derive_more::{Add, AddAssign, Deref, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign, Sum}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use std::convert::{TryFrom, TryInto}; @@ -14,7 +15,29 @@ const V2_REPLAY_PREFIX: u8 = 2; /// 1 SC = 10^24 Hastings /// use to_string_hastings() or to_string_siacoin() to display the value.\ // TODO Alright impl Add, Sub, PartialOrd, etc -#[derive(Copy, Clone, Debug, Deref, Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign, PartialEq, Eq, PartialOrd, Ord, Display, Default, From, Into, Sum)] +#[derive( + Copy, + Clone, + Debug, + Deref, + Add, + Sub, + Mul, + Div, + AddAssign, + SubAssign, + MulAssign, + DivAssign, + PartialEq, + Eq, + PartialOrd, + Ord, + Display, + Default, + From, + Into, + Sum, +)] pub struct Currency(pub u128); impl Currency { @@ -209,9 +232,8 @@ impl Encodable for SiafundElement { } } - /// As per, Sia Core a "SiacoinElement is a record of a SiacoinOutput within the state accumulator." -/// This type is effectively a "UTXO" in Bitcoin terms. +/// This type is effectively a "UTXO" in Bitcoin terms. /// A SiacoinElement can be combined with a SatisfiedPolicy to create a SiacoinInputV2. /// Ported from Sia Core: /// https://github.com/SiaFoundation/core/blob/b7ccbe54cccba5642c2bb9d721967214a4ba4e97/types/types.go#L619 @@ -391,11 +413,21 @@ pub struct SiacoinOutput { } impl From<(Currency, Address)> for SiacoinOutput { - fn from(tuple: (Currency, Address)) -> Self { SiacoinOutput { value: tuple.0, address: tuple.1 } } + fn from(tuple: (Currency, Address)) -> Self { + SiacoinOutput { + value: tuple.0, + address: tuple.1, + } + } } impl From<(Address, Currency)> for SiacoinOutput { - fn from(tuple: (Address, Currency)) -> Self { SiacoinOutput { address: tuple.0, value: tuple.1 } } + fn from(tuple: (Address, Currency)) -> Self { + SiacoinOutput { + address: tuple.0, + value: tuple.1, + } + } } #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] @@ -910,7 +942,7 @@ impl Encodable for V1ArbitraryData { } } /* -While implementing +While implementing , we faced two options. 1.) Treat every field as an Option<> 2.) Always initialize every empty field as a Vec<> @@ -1243,7 +1275,7 @@ impl V2TransactionBuilder { self } - /* Input is a special case becuase we cannot generate signatures until after fully constructing + /* Input is a special case becuase we cannot generate signatures until after fully constructing the transaction. Only the parent field is utilized while encoding the transaction to calculate the signature hash. Policy is included here to give any signing function or method a schema for producing a From d3106ab529427b1785df1687c73a240bd90397c6 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 23 Oct 2024 07:13:13 -0400 Subject: [PATCH 140/233] fix V2TransactionBuilder::build so consumer can correctly chain it with other methods --- src/types/transaction.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index e197413..de9efdc 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1334,19 +1334,20 @@ impl V2TransactionBuilder { self } - pub fn build(self) -> V2Transaction { + pub fn build(&mut self) -> V2Transaction { + let cloned = self.clone(); V2Transaction { - siacoin_inputs: self.siacoin_inputs, - siacoin_outputs: self.siacoin_outputs, - siafund_inputs: self.siafund_inputs, - siafund_outputs: self.siafund_outputs, - file_contracts: self.file_contracts, - file_contract_revisions: self.file_contract_revisions, - file_contract_resolutions: self.file_contract_resolutions, - attestations: self.attestations, - arbitrary_data: self.arbitrary_data, - new_foundation_address: self.new_foundation_address, - miner_fee: self.miner_fee, + siacoin_inputs: cloned.siacoin_inputs, + siacoin_outputs: cloned.siacoin_outputs, + siafund_inputs: cloned.siafund_inputs, + siafund_outputs: cloned.siafund_outputs, + file_contracts: cloned.file_contracts, + file_contract_revisions: cloned.file_contract_revisions, + file_contract_resolutions: cloned.file_contract_resolutions, + attestations: cloned.attestations, + arbitrary_data: cloned.arbitrary_data, + new_foundation_address: cloned.new_foundation_address, + miner_fee: cloned.miner_fee, } } } From 416414988cd3b6eed92ed7a241a9f0251525f09e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 13:16:02 -0400 Subject: [PATCH 141/233] fix Currency::COIN const definition --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index de9efdc..847511a 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -43,7 +43,7 @@ pub struct Currency(pub u128); impl Currency { pub const ZERO: Currency = Currency(0); - pub const COIN: Currency = Currency(1e24 as u128); + pub const COIN: Currency = Currency(1000000000000000000000000); /// The minimum amount of currency for a transaction output // FIXME this is a placeholder value until testing is complete From 09369a36f7ecf6c947ee1bb7c7bf3fd18abe4192 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 14:35:19 -0400 Subject: [PATCH 142/233] remove incomplete "SatisfyPolicy" trait - will explore this generalized concept in the future if resources become available --- src/types/spend_policy.rs | 47 +-------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 2e315f1..61cffe3 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -1,6 +1,6 @@ use crate::blake2b_internal::{public_key_leaf, sigs_required_leaf, standard_unlock_hash, timelock_leaf, Accumulator}; use crate::encoding::{Encodable, Encoder}; -use crate::types::{Address, Hash256, Preimage, PublicKey, SatisfiedPolicy, Signature, Specifier}; +use crate::types::{Address, Hash256, PublicKey, Specifier}; use nom::bytes::complete::{take_until, take_while, take_while_m_n}; use nom::character::complete::char; use nom::combinator::all_consuming; @@ -133,51 +133,6 @@ impl SpendPolicy { pub fn anyone_can_spend() -> Self { SpendPolicy::threshold(0, vec![]) } pub fn opacify(&self) -> Self { SpendPolicy::Opaque(self.address()) } - - pub fn satisfy(&self, data: T) -> Result { data.satisfy(self) } -} - -pub trait SatisfyPolicy { - fn satisfy(self, policy: &SpendPolicy) -> Result; -} - -impl SatisfyPolicy for Signature { - fn satisfy(self, policy: &SpendPolicy) -> Result { - match policy { - SpendPolicy::PublicKey(_) | SpendPolicy::UnlockConditions(_) => Ok(SatisfiedPolicy { - policy: policy.clone(), - signatures: vec![self], - preimages: vec![], - }), - _ => Err("Failed to satisfy. Policy is not PublicKey or UnlockConditions".to_string()), - } - } -} - -impl SatisfyPolicy for Preimage { - fn satisfy(self, policy: &SpendPolicy) -> Result { - match policy { - SpendPolicy::Hash(_) => Ok(SatisfiedPolicy { - policy: policy.clone(), - signatures: vec![], - preimages: vec![self], - }), - _ => Err("Failed to satisfy. Policy is not Hash".to_string()), - } - } -} - -impl SatisfyPolicy for () { - fn satisfy(self, policy: &SpendPolicy) -> Result { - match policy { - SpendPolicy::Above(_) | SpendPolicy::After(_) | SpendPolicy::Opaque(_) => Ok(SatisfiedPolicy { - policy: policy.clone(), - signatures: vec![], - preimages: vec![], - }), - _ => Err("Failed to satisfy. Policy is not Above, After or Opaque".to_string()), - } - } } impl SpendPolicy { From 487e1909a4f8a698a7715979c8d20adcf4ff7e37 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 14:37:55 -0400 Subject: [PATCH 143/233] fix transaction unit tests compilation --- src/tests/transaction.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 8cc0f1d..4c2d7de 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -29,7 +29,7 @@ mod test { } fn test_siacoin_currency_encode_v1() { - let currency: Currency = 1.into(); + let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); let expected = Hash256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); @@ -37,7 +37,7 @@ mod test { } fn test_siacoin_currency_encode_v2() { - let currency: Currency = 1.into(); + let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); let expected = Hash256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); @@ -62,7 +62,7 @@ mod test { fn test_siacoin_output_encode_v1() { let vout = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") .unwrap(), }; @@ -74,7 +74,7 @@ mod test { fn test_siacoin_output_encode_v2() { let vout = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") .unwrap(), }; @@ -96,7 +96,7 @@ mod test { let siacoin_element = SiacoinElement { state_element, siacoin_output: SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: Address::from_str( "addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a", ) @@ -376,7 +376,7 @@ mod test { merkle_proof: Some(vec![Hash256::default()]), }, siacoin_output: SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: policy.address(), }, maturity_height: 0, @@ -428,11 +428,11 @@ mod test { let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address0, }; let vout1 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address1, }; @@ -443,8 +443,8 @@ mod test { expiration_height: 1, renter_output: vout0, host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), + missed_host_value: 1u64.into(), + total_collateral: 1u64.into(), renter_public_key: pubkey0, host_public_key: pubkey1, revision_number: 1, @@ -476,11 +476,11 @@ mod test { let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address0, }; let vout1 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address1, }; @@ -491,8 +491,8 @@ mod test { expiration_height: 1, renter_output: vout0, host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), + missed_host_value: 1u64.into(), + total_collateral: 1u64.into(), renter_public_key: pubkey0, host_public_key: pubkey1, revision_number: 1, @@ -538,11 +538,11 @@ mod test { let address1 = Address::standard_address_v1(&pubkey1); let vout0 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address0, }; let vout1 = SiacoinOutput { - value: 1.into(), + value: 1u64.into(), address: address1, }; @@ -553,8 +553,8 @@ mod test { expiration_height: 1, renter_output: vout0, host_output: vout1, - missed_host_value: 1.into(), - total_collateral: 1.into(), + missed_host_value: 1u64.into(), + total_collateral: 1u64.into(), renter_public_key: pubkey0, host_public_key: pubkey1, revision_number: 1, From 4d4c602705a8e6fc86ce7d42aa72dfbd6384a710 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 14:39:22 -0400 Subject: [PATCH 144/233] add FeePolicy type to V2TransactionBuilder; doc comment regarding txfee rate calc; merge `impl V2TransactionBuilder` blocks --- src/types/transaction.rs | 42 +++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 847511a..d473a34 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1119,6 +1119,20 @@ impl Encodable for V2Transaction { } } +/// FeePolicy is data optionally included in V2TransactionBuilder to allow easier fee calculation. +/// Sia fee calculation can be complex in comparison to a typical UTXO protocol because the fee paid +/// to the miner is not simply the sum of the inputs minus the sum of the outputs. Instead, the +/// miner fee is a distinct field within the transaction, `miner_fee`. This `miner_fee` field is part +/// of signature calculation. As a result, you can build a transaction, produce signatures and preimages +/// for the inputs only to find out that the miner_fee hastings/byte rate is lower than expected. +/// Therefore a precise hastings/byte calculation requires correctly estimating the size of all +/// satifsfied inputs prior to producing signatures. +#[derive(Clone, Debug)] +pub enum FeePolicy { + HastingsPerByte(Currency), + HastingsFixed(Currency), +} + #[derive(Clone, Debug)] pub struct V2TransactionBuilder { pub siacoin_inputs: Vec, @@ -1132,19 +1146,9 @@ pub struct V2TransactionBuilder { pub arbitrary_data: Vec, pub new_foundation_address: Option
, pub miner_fee: Currency, -} - -impl V2TransactionBuilder { - /** - * "weight" is the size of the transaction in bytes. This can be used to estimate miner fees. - * The recommended method for calculating a suitable fee is to multiply the response of `/txpool/fee` API endpoint - * and the weight to get the fee in hastings. - */ - pub fn weight(&self) -> u64 { - let mut encoder = Encoder::default(); - self.encode(&mut encoder); - encoder.buffer.len() as u64 - } + // fee_policy is not part Sia consensus and it not encoded into any resulting transaction. + // fee_policy has no effect unless a helper like `ApiClientHelpers::fund_tx_single_source` utilizes it. + pub fee_policy: Option, } impl Encodable for V2TransactionBuilder { @@ -1217,6 +1221,7 @@ impl V2TransactionBuilder { arbitrary_data: Vec::new(), new_foundation_address: None, miner_fee: Currency::ZERO, + fee_policy: None, } } @@ -1275,6 +1280,17 @@ impl V2TransactionBuilder { self } + /** + * "weight" is the size of the transaction in bytes. This can be used to estimate miner fees. + * The recommended method for calculating a suitable fee is to multiply the response of + * `/txpool/fee` API endpoint and the weight to get the fee in hastings. + */ + pub fn weight(&self) -> u64 { + let mut encoder = Encoder::default(); + self.encode(&mut encoder); + encoder.buffer.len() as u64 + } + /* Input is a special case becuase we cannot generate signatures until after fully constructing the transaction. Only the parent field is utilized while encoding the transaction to calculate the signature hash. From a0a2ecc3ea25dff3cb6a8fc8b458fe6146f2ac5e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:21:04 -0400 Subject: [PATCH 145/233] change Preimage to fixed sized array instead of Vec; accounts for Sia Core validation changes --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index d473a34..e89fdb2 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -122,7 +122,7 @@ impl<'a> Encodable for CurrencyVersion<'a> { } } -pub type Preimage = Vec; +pub type Preimage = [u8; 32]; #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(deny_unknown_fields)] From 3be905b858e6bae67fef1f38d2ed089ee2e4a49c Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:36:28 -0400 Subject: [PATCH 146/233] fix transaction encoding tests to account for Vec -> [u8;32] preimage change --- src/tests/transaction.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 4c2d7de..7dfabbf 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -2,9 +2,9 @@ mod test { use crate::encoding::Encoder; use crate::types::{Address, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, Hash256, Keypair, - PublicKey, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, - SiacoinOutputVersion, Signature, SpendPolicy, StateElement, UnlockCondition, V2FileContract, - V2FileContractElement, V2Transaction}; + Preimage, PublicKey, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, + SiacoinOutput, SiacoinOutputVersion, Signature, SpendPolicy, StateElement, UnlockCondition, + V2FileContract, V2FileContractElement, V2Transaction}; use std::convert::TryFrom; use std::str::FromStr; From 394829886f090c5a49b4195da9da4377c1488cb1 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:38:14 -0400 Subject: [PATCH 147/233] again fix transaction encoding tests to account for Vec -> [u8;32] preimage change --- src/tests/transaction.rs | 46 ++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 7dfabbf..6478a94 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -185,11 +185,12 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![], - preimages: vec![vec![]], // vec!(1u8, 2u8, 3u8, 4u8) + preimages: vec![Preimage::default()], }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:86b4b84950016d711732617d2501bd22e41614535f2705a65bd5b0e95c992a44").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:51eb259ed69313a81d72ea5ee1eb7c7111469e66720f2faef0a19054f959d375").unwrap(); assert_eq!(hash, expected); } @@ -197,29 +198,36 @@ mod test { fn test_satisfied_policy_encode_hash_frivulous_signature() { let policy = SpendPolicy::Hash(Hash256::default()); + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); + let satisfied_policy = SatisfiedPolicy { policy, signatures: vec!(Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), - preimages: vec!(vec!(1u8, 2u8, 3u8, 4u8)), + preimages: vec!(preimage), }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); assert_eq!(hash, expected); } fn test_satisfied_policy_encode_hash() { let policy = SpendPolicy::Hash(Hash256::default()); + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + preimages: vec![preimage], }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:7424653d0ca3ffded9a029bebe75f9ae9c99b5f284e23e9d07c0b03456f724f9").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); assert_eq!(hash, expected); } @@ -289,15 +297,17 @@ mod test { n: 1, of: vec![sub_policy], }; - + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + preimages: vec![preimage], }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:50f4808b0661f56842472aed259136a43ed2bd7d59a88a3be28de9883af4a92d").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:d7270a87868f9127bf99cc33a3548b669afb308c49760c840d7a15e8066f8c47").unwrap(); assert_eq!(hash, expected); } @@ -317,14 +327,17 @@ mod test { let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![signature], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + preimages: vec![preimage], }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:c835e516bbf76602c897a9160c17bfe0e4a8bc9044f62b3e5e45a381232a2f86").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:4da166c66b22c6cc825253d4e9b4f5319549b82ade6f9c8a037d8e7a4acfcdfa").unwrap(); assert_eq!(hash, expected); } @@ -344,10 +357,12 @@ mod test { let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![signature], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + preimages: vec![preimage], }; let hash = Encoder::encode_and_hash(&satisfied_policy); @@ -361,11 +376,13 @@ mod test { n: 1, of: vec![sub_policy], }; + let mut preimage = [0u8; 32]; + preimage[..4].copy_from_slice(&[1, 2, 3, 4]); let satisfied_policy = SatisfiedPolicy { policy: policy.clone(), signatures: vec![], - preimages: vec![vec![1u8, 2u8, 3u8, 4u8]], + preimages: vec![preimage], }; let vin = SiacoinInputV2 { @@ -385,7 +402,8 @@ mod test { }; let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:a8ab11b91ee19ce68f2d608bd4d19212841842f0c50151ae4ccb8e9db68cd6c4").unwrap(); + // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] + let expected = Hash256::try_from("h:10497f5864991eb72c2bc49ad61a5afdd068bb48dca9db825b5adb94b49b9cbe").unwrap(); assert_eq!(hash, expected); } From d4988a19ddc6030f7ed105ff076dd3143ca88237 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:38:45 -0400 Subject: [PATCH 148/233] fix ApiClientHelpers::select_unspent_outputs doc comment --- src/transport/client/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 37ae54c..d210e31 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -49,13 +49,13 @@ pub trait ApiClientHelpers: ApiClient { /// # Arguments /// /// * `unspent_outputs` - A vector of `SiacoinElement`s representing unspent Siacoin outputs. - /// * `total_amount` - The total amount (in u128) required for the selection. + /// * `total_amount` - The total amount required for the selection. Should generally be the sum of the outputs and miner fee. /// /// # Returns /// /// This function returns `Result<(Vec, Currency), ApiClientHelpersError>`: /// * `Ok((Vec, Currency))` - A tuple containing, a vector of the selected unspent outputs and the change amount. - /// * `Err(MmError)` - An error is returned if the available outputs cannot meet the required amount or a transport error is encountered. + /// * `Err(ApiClientHelpersError)` - An error is returned if the available outputs cannot meet the required amount or a transport error is encountered. async fn select_unspent_outputs( &self, address: &Address, From 98e9c7d9e51599a27249edac9296a9878249117e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:41:01 -0400 Subject: [PATCH 149/233] add brief doc comment for type Preimage --- src/types/transaction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index e89fdb2..2e76992 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -122,6 +122,8 @@ impl<'a> Encodable for CurrencyVersion<'a> { } } +/// Preimage is a 32-byte array representing the preimage of a hash used in Sia's SpendPolicy::Hash +/// Used to allow HLTC-style hashlock contracts in Sia pub type Preimage = [u8; 32]; #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] From 2614f191d27e14e942a30d766ff3a7bc747e691b Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 24 Oct 2024 15:57:51 -0400 Subject: [PATCH 150/233] typo in doc comment --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 2e76992..382a891 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1128,7 +1128,7 @@ impl Encodable for V2Transaction { /// of signature calculation. As a result, you can build a transaction, produce signatures and preimages /// for the inputs only to find out that the miner_fee hastings/byte rate is lower than expected. /// Therefore a precise hastings/byte calculation requires correctly estimating the size of all -/// satifsfied inputs prior to producing signatures. +/// satisfied inputs prior to producing signatures. #[derive(Clone, Debug)] pub enum FeePolicy { HastingsPerByte(Currency), From 5d472f2215a18b2689a03901b2aafdecc49d84f3 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 25 Oct 2024 11:53:25 -0400 Subject: [PATCH 151/233] update fund_tx_single_source txfee handling; edit doc comment of fund_tx_single_source --- src/transport/client/helpers.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index d210e31..2ed6ed9 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -91,28 +91,31 @@ pub trait ApiClientHelpers: ApiClient { } /// Fund a transaction with utxos from the given address. - /// Will add inputs from the given address until the total amount from outputs is reached. - /// Will add the change amount to the transaction outputs - /// Returns the difference between inputs and outputs that would be paid to the miner. - /// See `select_unspent_outputs` for more details. + /// This should generally be used only after all outputs and miner_fee have been added to the builder. + /// Assumes no file contracts or resolutions. This is a helper designed for Komodo DeFi Framework. + /// Adds inputs from the given address until the total amount from outputs and miner_fee is reached. + /// Adds the change amount to the transaction outputs + /// See `select_unspent_outputs` for more details on UTXO selection. /// # Arguments /// * `tx_builder` - A mutable reference to a `V2TransactionBuilder. /// * `public_key` - The public key of the address to spend utxos from. - /// * `miner_fee` - The amount to pay to the miner. /// # Returns - /// * `Ok(Currency)` - The difference between inputs and outputs that would be paid to the miner. + /// * `Ok(())` - The transaction builder has been successfully funded + /// * `Err(ApiClientHelpersError)` - An error is returned if the available outputs cannot meet + /// the required amount or a transport error is encountered. async fn fund_tx_single_source( &self, tx_builder: &mut V2TransactionBuilder, public_key: &PublicKey, - miner_fee: Currency, ) -> Result<(), ApiClientHelpersError> { let address = public_key.address(); let outputs_total: Currency = tx_builder.siacoin_outputs.iter().map(|output| output.value).sum(); // select utxos from public key's address that total at least the sum of outputs and miner fee - let (selected_utxos, change) = self.select_unspent_outputs(&address, outputs_total + miner_fee).await?; - // FIXME OMAR take a look + let (selected_utxos, change) = self + .select_unspent_outputs(&address, outputs_total + tx_builder.miner_fee) + .await?; + // add selected utxos as inputs to the transaction for utxo in &selected_utxos { tx_builder.add_siacoin_input(utxo.clone(), SpendPolicy::PublicKey(public_key.clone())); From 87e69b1dbbee087ea56ab55276401b5f36bd823f Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 25 Oct 2024 14:44:00 -0400 Subject: [PATCH 152/233] impl TryFrom<&[u8]> for Hash256 --- src/types/hash.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/types/hash.rs b/src/types/hash.rs index 57b08a1..be65638 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -12,6 +12,8 @@ pub enum ParseHashError { InvalidHex(String), #[error("invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidLength(String), + #[error("invalid slice length: expected 32 byte slice, found {0} byte slice")] + InvalidSliceLength(usize), } #[derive(Clone, Eq, PartialEq)] pub struct Hash256(pub [u8; 32]); @@ -107,6 +109,21 @@ impl TryFrom<&str> for Hash256 { fn try_from(value: &str) -> Result { Hash256::from_str(value) } } +impl TryFrom<&[u8]> for Hash256 { + type Error = ParseHashError; + + fn try_from(slice: &[u8]) -> Result { + let slice_len = slice.len(); + if slice_len == 32 { + let mut array = [0u8; 32]; + array.copy_from_slice(slice); + Ok(Hash256(array)) + } else { + Err(ParseHashError::InvalidSliceLength(slice_len)) + } + } +} + #[cfg(test)] mod tests { use super::*; From 273dd37dfe291c93bb8de440eaeb908afd22d2dd Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 12:55:42 -0400 Subject: [PATCH 153/233] change Preimage from type alias to newtype struc --- src/types/transaction.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 382a891..6a801e7 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -124,7 +124,9 @@ impl<'a> Encodable for CurrencyVersion<'a> { /// Preimage is a 32-byte array representing the preimage of a hash used in Sia's SpendPolicy::Hash /// Used to allow HLTC-style hashlock contracts in Sia -pub type Preimage = [u8; 32]; +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, From, Into)] +#[serde(transparent)] +pub struct Preimage(pub [u8; 32]); #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(deny_unknown_fields)] @@ -160,7 +162,7 @@ impl Encodable for SatisfiedPolicy { }, SpendPolicy::Hash(_) => { if *prei < sp.preimages.len() { - encoder.write_len_prefixed_bytes(&sp.preimages[*prei]); + encoder.write_len_prefixed_bytes(&sp.preimages[*prei].0.to_vec()); *prei += 1; } else { // Sia Go code panics here but our code assumes encoding will always be successful From c1d9d81bd1038f83db102626340080fb8d822429 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 12:57:35 -0400 Subject: [PATCH 154/233] add enum PreimageError impl TryFrom<&[u8]> for Preimage and From for Vec --- src/types/transaction.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 6a801e7..c5c21d1 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -8,6 +8,7 @@ use serde_json::Value; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::str::FromStr; +use thiserror::Error; const V2_REPLAY_PREFIX: u8 = 2; @@ -128,6 +129,31 @@ impl<'a> Encodable for CurrencyVersion<'a> { #[serde(transparent)] pub struct Preimage(pub [u8; 32]); +#[derive(Debug, Error)] +pub enum PreimageError { + #[error("PreimageError: failed to convert from slice")] + InvalidSliceLength(usize), +} + +impl From for Vec { + fn from(preimage: Preimage) -> Self { preimage.0.to_vec() } +} + +impl TryFrom<&[u8]> for Preimage { + type Error = PreimageError; + + fn try_from(slice: &[u8]) -> Result { + let slice_len = slice.len(); + if slice_len == 32 { + let mut array = [0u8; 32]; + array.copy_from_slice(slice); + Ok(Preimage(array)) + } else { + Err(PreimageError::InvalidSliceLength(slice_len)) + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(deny_unknown_fields)] pub struct SatisfiedPolicy { From 14a8f90798a293ba17f378b1519fe36b8a20d825 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 12:59:42 -0400 Subject: [PATCH 155/233] remove redundant `impl TryFrom<&str> for Hash256`. FromStr is implemented for Hash256. --- src/types/hash.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index be65638..db06a94 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -103,12 +103,6 @@ impl FromStr for Hash256 { } } -impl TryFrom<&str> for Hash256 { - type Error = ParseHashError; - - fn try_from(value: &str) -> Result { Hash256::from_str(value) } -} - impl TryFrom<&[u8]> for Hash256 { type Error = ParseHashError; From 97fa3f851a8f2c67fd9a7cca572aa8b97d7dc94c Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 13:01:39 -0400 Subject: [PATCH 156/233] add verbosity to ParseHashError display impls --- src/types/hash.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index db06a94..bdb0159 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -6,13 +6,13 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum ParseHashError { - #[error("invalid prefix: expected 32 byte hex string prefixed with 'h:', found {0}")] + #[error("Hash256 invalid prefix: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidPrefix(String), - #[error("invalid hex: expected 32 byte hex string prefixed with 'h:', found {0}")] + #[error("Hash256 invalid hex: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidHex(String), - #[error("invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] + #[error("Hash256 invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] InvalidLength(String), - #[error("invalid slice length: expected 32 byte slice, found {0} byte slice")] + #[error("Hash256 invalid slice length: expected 32 byte slice, found {0} byte slice")] InvalidSliceLength(usize), } #[derive(Clone, Eq, PartialEq)] From 062aa6eb89b7c55ef4306a4f6bfdf2bfff53d4e4 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 17:32:44 -0400 Subject: [PATCH 157/233] Add TransactionId type alias; Add SiacoinOutputId::new impl Encodable for SiacoinOutputId derive From, Into for SiacoinOutputId instead of explicit impl --- src/types/transaction.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index c5c21d1..8433ff7 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -380,9 +380,30 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { } } -#[derive(Clone, Debug, PartialEq)] +/// A Sia transaction id aka "txid" +/// This could be a newtype like SiacoinOutputId with custom serde, but we have no use for this beyond +/// making SiacoinOutputId::new more explicit. +/// Sia Go uses "txid:" prefix for this type, but walletd API generally represents this with the +/// more generic "h:" prefix. related: https://github.com/SiaFoundation/core/pull/199 +pub type TransactionId = Hash256; + +#[derive(Clone, Debug, PartialEq, From, Into)] pub struct SiacoinOutputId(pub Hash256); +impl SiacoinOutputId { + pub fn new(txid: TransactionId, index: u64) -> Self { + let mut encoder = Encoder::default(); + encoder.write_distinguisher("id/siacoinoutput"); + txid.encode(&mut encoder); + encoder.write_u64(index); + SiacoinOutputId(encoder.hash()) + } +} + +impl Encodable for SiacoinOutputId { + fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0 .0); } +} + impl<'de> Deserialize<'de> for SiacoinOutputId { fn deserialize(deserializer: D) -> Result where @@ -428,14 +449,6 @@ impl fmt::Display for SiacoinOutputId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "scoid:{:02x}", self.0) } } -impl From for Hash256 { - fn from(sia_hash: SiacoinOutputId) -> Self { sia_hash.0 } -} - -impl From for SiacoinOutputId { - fn from(h256: Hash256) -> Self { SiacoinOutputId(h256) } -} - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct SiacoinOutput { pub value: Currency, @@ -1084,7 +1097,7 @@ impl V2Transaction { encoder.hash() } - pub fn txid(&self) -> Hash256 { + pub fn txid(&self) -> TransactionId { let mut encoder = Encoder::default(); encoder.write_distinguisher("id/transaction"); self.encode(&mut encoder); From 3802f5fbda941ed02ed8d730cfcf463c4f81bb31 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 17:56:49 -0400 Subject: [PATCH 158/233] Fix test compilation failure due to removal of `impl TryFrom<&str> for Hash256`; Use from_str instead. add unit test test_siacoin_output_id_new derive Default for Preimage and fix related tests --- src/blake2b_internal.rs | 19 ++++--- src/encoding.rs | 20 +++---- src/tests/encoding.rs | 23 ++++---- src/tests/spend_policy.rs | 5 +- src/tests/transaction.rs | 113 ++++++++++++++++++++------------------ src/types/hash.rs | 22 ++++---- src/types/transaction.rs | 2 +- 7 files changed, 106 insertions(+), 98 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 2b3a9ff..39fb04a 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -3,7 +3,7 @@ use blake2b_simd::Params; use std::default::Default; #[cfg(test)] use hex; -#[cfg(test)] use std::convert::{TryFrom, TryInto}; +#[cfg(test)] use std::convert::TryInto; const LEAF_HASH_PREFIX: [u8; 1] = [0u8]; const NODE_HASH_PREFIX: [u8; 1] = [1u8]; @@ -154,6 +154,7 @@ fn hash_blake2b_pair(prefix: &[u8], leaf1: &[u8], leaf2: &[u8]) -> Hash256 { #[cfg(test)] mod test { use super::*; + use std::str::FromStr; cross_target_tests! { fn test_accumulator_new() { @@ -184,7 +185,7 @@ mod test { let sigs_required_leaf = sigs_required_leaf(1u64); accumulator.add_leaf(sigs_required_leaf); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(accumulator.root(), expected); } @@ -205,7 +206,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(root, expected) } @@ -233,7 +234,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::from_str("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(root, expected) } @@ -261,7 +262,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::from_str("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(root, expected) } @@ -272,7 +273,7 @@ mod test { .unwrap(); let hash = standard_unlock_hash(&pubkey); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -287,7 +288,7 @@ mod test { .unwrap(); let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -305,7 +306,7 @@ mod test { fn test_hash_blake2b_single() { let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::from_str("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } @@ -316,7 +317,7 @@ mod test { .unwrap(); let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let expected = Hash256::try_from("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::from_str("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } } diff --git a/src/encoding.rs b/src/encoding.rs index 907c3e9..d6bdf7e 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -60,13 +60,13 @@ impl Encodable for Hash256 { #[cfg(test)] mod test { use super::*; - use std::convert::TryFrom; + use std::str::FromStr; cross_target_tests! { fn test_encoder_default_hash() { assert_eq!( Encoder::default().hash(), - Hash256::try_from("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + Hash256::from_str("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() ) } @@ -75,7 +75,7 @@ mod test { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - Hash256::try_from("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() + Hash256::from_str("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() ); } @@ -84,7 +84,7 @@ mod test { encoder.write_u8(1); assert_eq!( encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -93,7 +93,7 @@ mod test { encoder.write_u64(1); assert_eq!( encoder.hash(), - Hash256::try_from("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() + Hash256::from_str("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() ); } @@ -102,7 +102,7 @@ mod test { encoder.write_distinguisher("test"); assert_eq!( encoder.hash(), - Hash256::try_from("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() + Hash256::from_str("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() ); } @@ -111,7 +111,7 @@ mod test { encoder.write_bool(true); assert_eq!( encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -120,14 +120,14 @@ mod test { encoder.write_bool(true); assert_eq!( encoder.hash(), - Hash256::try_from("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); encoder.reset(); encoder.write_bool(false); assert_eq!( encoder.hash(), - Hash256::try_from("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() + Hash256::from_str("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() ); } @@ -139,7 +139,7 @@ mod test { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - Hash256::try_from("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() + Hash256::from_str("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() ); } } diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 4e86280..d825dc3 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -3,7 +3,6 @@ mod test { use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::Encoder; use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition}; - use std::convert::TryFrom; use std::str::FromStr; cross_target_tests! { @@ -19,7 +18,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::from_str("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(hash, expected); } @@ -35,7 +34,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::from_str("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(hash, expected); } @@ -43,7 +42,7 @@ mod test { let policy = SpendPolicy::above(1); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); + let expected = Hash256::from_str("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -55,7 +54,7 @@ mod test { fn test_spend_policy_encode_after() { let policy = SpendPolicy::after(1); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); + let expected = Hash256::from_str("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -72,7 +71,7 @@ mod test { let policy = SpendPolicy::PublicKey(pubkey); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); + let expected = Hash256::from_str("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -82,11 +81,11 @@ mod test { } fn test_spend_policy_encode_hash() { - let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::Hash(hash); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); + let expected = Hash256::from_str("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -102,7 +101,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::try_from("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); + let expected = Hash256::from_str("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -142,7 +141,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = Encoder::encode_and_hash(&unlock_condition); - let expected = Hash256::try_from("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); + let expected = Hash256::from_str("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); assert_eq!(hash, expected); } @@ -153,7 +152,7 @@ mod test { .unwrap(); let hash = Encoder::encode_and_hash(&public_key); - let expected = Hash256::try_from("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); + let expected = Hash256::from_str("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); assert_eq!(hash, expected); } @@ -165,7 +164,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey.clone()], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::try_from("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected); let hash = standard_unlock_hash(&pubkey); assert_eq!(hash, expected); diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index d44866c..55b269a 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod test { use crate::types::{Address, Hash256, PublicKey, SpendPolicy, UnlockCondition, UnlockKey}; - use std::convert::TryFrom; use std::str::FromStr; cross_target_tests! { @@ -57,7 +56,7 @@ mod test { "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" } ); - let hash = Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Hash(hash); @@ -89,7 +88,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let j = json!( diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 6478a94..ea25de0 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -3,9 +3,8 @@ mod test { use crate::encoding::Encoder; use crate::types::{Address, Attestation, Currency, CurrencyVersion, FileContractRevisionV2, Hash256, Keypair, Preimage, PublicKey, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, - SiacoinOutput, SiacoinOutputVersion, Signature, SpendPolicy, StateElement, UnlockCondition, - V2FileContract, V2FileContractElement, V2Transaction}; - use std::convert::TryFrom; + SiacoinOutput, SiacoinOutputId, SiacoinOutputVersion, Signature, SpendPolicy, StateElement, + UnlockCondition, V2FileContract, V2FileContractElement, V2Transaction}; use std::str::FromStr; cross_target_tests! { @@ -17,14 +16,14 @@ mod test { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000") + parent_id: Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000") .unwrap() .into(), unlock_condition, }; let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); + let expected = Hash256::from_str("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); assert_eq!(hash, expected); } @@ -32,7 +31,7 @@ mod test { let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::try_from("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); + let expected = Hash256::from_str("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); assert_eq!(hash, expected); } @@ -40,7 +39,7 @@ mod test { let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::try_from("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -48,7 +47,7 @@ mod test { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::try_from("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); + let expected = Hash256::from_str("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); assert_eq!(hash, expected); } @@ -56,7 +55,7 @@ mod test { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::try_from("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); + let expected = Hash256::from_str("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); assert_eq!(hash, expected); } @@ -68,7 +67,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); - let expected = Hash256::try_from("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); + let expected = Hash256::from_str("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); assert_eq!(hash, expected); } @@ -80,17 +79,17 @@ mod test { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); - let expected = Hash256::try_from("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); + let expected = Hash256::from_str("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); assert_eq!(hash, expected); } fn test_siacoin_element_encode() { let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let siacoin_element = SiacoinElement { @@ -106,34 +105,34 @@ mod test { }; let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = Hash256::try_from("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); + let expected = Hash256::from_str("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); assert_eq!(hash, expected); } fn test_state_element_encode() { let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::try_from("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); + let expected = Hash256::from_str("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); assert_eq!(hash, expected); } fn test_state_element_encode_null_merkle_proof() { let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: None, }; let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::try_from("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); + let expected = Hash256::from_str("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); assert_eq!(hash, expected); } @@ -144,7 +143,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::try_from("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); + let expected = Hash256::from_str("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); assert_eq!(hash, expected); } @@ -153,7 +152,7 @@ mod test { &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let hash = Encoder::encode_and_hash(&signature); - let expected = Hash256::try_from("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); + let expected = Hash256::from_str("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); assert_eq!(hash, expected); } @@ -175,7 +174,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); + let expected = Hash256::from_str("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); assert_eq!(hash, expected); } @@ -190,7 +189,7 @@ mod test { let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:51eb259ed69313a81d72ea5ee1eb7c7111469e66720f2faef0a19054f959d375").unwrap(); + let expected = Hash256::from_str("h:51eb259ed69313a81d72ea5ee1eb7c7111469e66720f2faef0a19054f959d375").unwrap(); assert_eq!(hash, expected); } @@ -205,12 +204,12 @@ mod test { policy, signatures: vec!(Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), - preimages: vec!(preimage), + preimages: vec!(preimage.into()), }; let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); + let expected = Hash256::from_str("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); assert_eq!(hash, expected); } @@ -222,12 +221,12 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![], - preimages: vec![preimage], + preimages: vec![preimage.into()], }; let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); + let expected = Hash256::from_str("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); assert_eq!(hash, expected); } @@ -251,7 +250,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); + let expected = Hash256::from_str("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); assert_eq!(hash, expected); } @@ -287,7 +286,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); + let expected = Hash256::from_str("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); assert_eq!(hash, expected); } @@ -302,12 +301,12 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![], - preimages: vec![preimage], + preimages: vec![preimage.into()], }; let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:d7270a87868f9127bf99cc33a3548b669afb308c49760c840d7a15e8066f8c47").unwrap(); + let expected = Hash256::from_str("h:d7270a87868f9127bf99cc33a3548b669afb308c49760c840d7a15e8066f8c47").unwrap(); assert_eq!(hash, expected); } @@ -321,7 +320,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -332,12 +331,12 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![signature], - preimages: vec![preimage], + preimages: vec![preimage.into()], }; let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:4da166c66b22c6cc825253d4e9b4f5319549b82ade6f9c8a037d8e7a4acfcdfa").unwrap(); + let expected = Hash256::from_str("h:4da166c66b22c6cc825253d4e9b4f5319549b82ade6f9c8a037d8e7a4acfcdfa").unwrap(); assert_eq!(hash, expected); } @@ -351,7 +350,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::try_from("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); let signature = Signature::from_bytes( @@ -362,11 +361,11 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, signatures: vec![signature], - preimages: vec![preimage], + preimages: vec![preimage.into()], }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::try_from("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); + let expected = Hash256::from_str("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); assert_eq!(hash, expected); } @@ -382,7 +381,7 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy: policy.clone(), signatures: vec![], - preimages: vec![preimage], + preimages: vec![preimage.into()], }; let vin = SiacoinInputV2 { @@ -403,7 +402,7 @@ mod test { let hash = Encoder::encode_and_hash(&vin); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::try_from("h:10497f5864991eb72c2bc49ad61a5afdd068bb48dca9db825b5adb94b49b9cbe").unwrap(); + let expected = Hash256::from_str("h:10497f5864991eb72c2bc49ad61a5afdd068bb48dca9db825b5adb94b49b9cbe").unwrap(); assert_eq!(hash, expected); } @@ -423,7 +422,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&attestation); - let expected = Hash256::try_from("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); + let expected = Hash256::from_str("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); assert_eq!(hash, expected); } @@ -471,7 +470,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = Hash256::try_from("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); + let expected = Hash256::from_str("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); assert_eq!(hash, expected); } @@ -519,11 +518,11 @@ mod test { }; let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -533,7 +532,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = Hash256::try_from("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); + let expected = Hash256::from_str("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); assert_eq!(hash, expected); } @@ -581,11 +580,11 @@ mod test { }; let state_element = StateElement { - id: Hash256::try_from("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, merkle_proof: Some(vec![ - Hash256::try_from("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::try_from("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ]), }; @@ -600,7 +599,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = Hash256::try_from("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); + let expected = Hash256::from_str("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); assert_eq!(hash, expected); } @@ -659,7 +658,7 @@ mod test { let tx = serde_json::from_value::(j).unwrap(); let hash = tx.input_sig_hash(); - let expected = Hash256::try_from("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); + let expected = Hash256::from_str("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); assert_eq!(hash, expected); } @@ -728,5 +727,15 @@ mod test { let sig: Signature = keypair.sign(&sig_hash.0); assert_eq!(tx.siacoin_inputs[0].satisfied_policy.signatures[0], sig); } + + fn test_siacoin_output_id_new() { + let txid = Hash256::from_str("h:31be0badc64d40fbcb91b63835c07d75ab49addd1fc1d839b8415e1e5ff38cb5").unwrap(); + let output_index = 0u64; + let output_id = SiacoinOutputId::new(txid, output_index); + let expected = SiacoinOutputId( + Hash256::from_str("h:47b2ceee0a9e246d5f997129a250ecb3d0917f5e844989d520e246145349d292").unwrap(), + ); + assert_eq!(output_id, expected); + } } } diff --git a/src/types/hash.rs b/src/types/hash.rs index bdb0159..250f70c 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -125,33 +125,33 @@ mod tests { cross_target_tests! { fn test_default() { - let hash = Hash256::try_from("h:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("h:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()); } fn test_valid() { - let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_display() { - let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_debug() { - let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); assert_eq!(format!("{:?}", hash), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_serialize() { - let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); let serialized = serde_json::to_string(&hash).unwrap(); assert_eq!(&serialized, r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); } fn test_deserialize() { - let hash = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); let deserialized: Hash256 = serde_json::from_str(r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); assert_eq!(deserialized, hash); } @@ -163,7 +163,7 @@ mod tests { fn test_missing_prefix() { let test_case = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::try_from(test_case).expect_err("no prefix"); + let err = Hash256::from_str(test_case).expect_err("no prefix"); match err { ParseHashError::InvalidPrefix(ref e) if test_case == e => (), _ => panic!("unexpected error: {:?}", err), @@ -172,7 +172,7 @@ mod tests { fn test_corrupt_prefix() { let test_case = ":c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::try_from(test_case).expect_err("no prefix"); + let err = Hash256::from_str(test_case).expect_err("no prefix"); match err { ParseHashError::InvalidPrefix(ref e) if test_case == e => (), _ => panic!("unexpected error: {:?}", err), @@ -181,7 +181,7 @@ mod tests { fn test_wrong_prefix() { let test_case = "i:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::try_from(test_case).expect_err("wrong prefix"); + let err = Hash256::from_str(test_case).expect_err("wrong prefix"); match err { ParseHashError::InvalidPrefix(ref e) if test_case == e => (), _ => panic!("unexpected error: {:?}", err), @@ -189,7 +189,7 @@ mod tests { } fn test_invalid_hex() { - let err = Hash256::try_from("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); + let err = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); let expected = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg"; match err { ParseHashError::InvalidHex(ref e) if expected == e => (), @@ -198,7 +198,7 @@ mod tests { } fn test_invalid_length() { - let err = Hash256::try_from("h:badc0de").expect_err("invalid length"); + let err = Hash256::from_str("h:badc0de").expect_err("invalid length"); let expected = "badc0de"; match err { ParseHashError::InvalidLength(ref e) if expected == e => (), diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 8433ff7..ce2f89e 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -125,7 +125,7 @@ impl<'a> Encodable for CurrencyVersion<'a> { /// Preimage is a 32-byte array representing the preimage of a hash used in Sia's SpendPolicy::Hash /// Used to allow HLTC-style hashlock contracts in Sia -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, From, Into)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, From, Into)] #[serde(transparent)] pub struct Preimage(pub [u8; 32]); From f41aa3edf2f262a93f2a560117a7e8bec31736bc Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 17:57:24 -0400 Subject: [PATCH 159/233] remove unused `impl Encodable for SiacoinOutputId` --- src/types/transaction.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index ce2f89e..d84786d 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -400,10 +400,6 @@ impl SiacoinOutputId { } } -impl Encodable for SiacoinOutputId { - fn encode(&self, encoder: &mut Encoder) { encoder.write_slice(&self.0 .0); } -} - impl<'de> Deserialize<'de> for SiacoinOutputId { fn deserialize(deserializer: D) -> Result where From 050216984b03e10d9fb0ea2304ff1740ab3da5fd Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 18:57:42 -0400 Subject: [PATCH 160/233] Use u32 instead of u64 for output index. Indexing via tx.siacoin_outputs[vout_index] may be a common pattern. WASM usize is u32 which would require doing lossy u64->u32 --- src/tests/transaction.rs | 2 +- src/types/transaction.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index ea25de0..e18c101 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -730,7 +730,7 @@ mod test { fn test_siacoin_output_id_new() { let txid = Hash256::from_str("h:31be0badc64d40fbcb91b63835c07d75ab49addd1fc1d839b8415e1e5ff38cb5").unwrap(); - let output_index = 0u64; + let output_index = 0u32; let output_id = SiacoinOutputId::new(txid, output_index); let expected = SiacoinOutputId( Hash256::from_str("h:47b2ceee0a9e246d5f997129a250ecb3d0917f5e844989d520e246145349d292").unwrap(), diff --git a/src/types/transaction.rs b/src/types/transaction.rs index d84786d..b1d48b5 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -391,11 +391,11 @@ pub type TransactionId = Hash256; pub struct SiacoinOutputId(pub Hash256); impl SiacoinOutputId { - pub fn new(txid: TransactionId, index: u64) -> Self { + pub fn new(txid: TransactionId, index: u32) -> Self { let mut encoder = Encoder::default(); encoder.write_distinguisher("id/siacoinoutput"); txid.encode(&mut encoder); - encoder.write_u64(index); + encoder.write_u64(index as u64); SiacoinOutputId(encoder.hash()) } } From 4818788bf3b88be1cbeda785edabf618ec603d6c Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 19:39:13 -0400 Subject: [PATCH 161/233] add ApiClientHelpers::utxo_from_txid helper --- src/transport/client/helpers.rs | 75 ++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 2ed6ed9..0d799fe 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,7 +1,8 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, - GetAddressUtxosRequest}; -use crate::types::{Address, Currency, PublicKey, SiacoinElement, SpendPolicy, V2TransactionBuilder}; + GetAddressUtxosRequest, GetEventRequest}; +use crate::types::{Address, Currency, Event, EventDataWrapper, PublicKey, SiacoinElement, SiacoinOutputId, + SpendPolicy, TransactionId, V2TransactionBuilder}; use async_trait::async_trait; use thiserror::Error; @@ -15,6 +16,22 @@ pub enum ApiClientHelpersError { ApiClientError(#[from] ApiClientError), } +#[derive(Debug, Error)] +pub enum UtxoFromTxidError { + #[error("utxo_from_txid: failed to fetch event {0}")] + FetchEvent(ApiClientError), + #[error("utxo_from_txid: invalid event variant {0:?}")] + EventVariant(Event), + #[error("utxo_from_txid: output index out of bounds txid: {txid:?} index: {index:?}")] + OutputIndexOutOfBounds { txid: TransactionId, index: u32 }, + #[error("utxo_from_txid: get_unspent_outputs helper failed {0}")] + FetchUtxos(ApiClientError), + #[error("utxo_from_txid: output not found txid: {txid:?} index: {index:?}")] + NotFound { txid: TransactionId, index: u32 }, + #[error("utxo_from_txid: found duplicate utxo txid: {txid:?} index: {index:?}")] + DuplicateUtxoFound { txid: TransactionId, index: u32 }, +} + /// Helper methods for the ApiClient trait /// These generally provide higher level functionality than the base ApiClient trait /// This crate is focused on catering to the Komodo Defi Framework integration @@ -128,4 +145,58 @@ pub trait ApiClientHelpers: ApiClient { Ok(()) } + + /// Fetches a SiacoinElement(a UTXO) from a TransactionId and Index + /// Walletd doesn't currently offer an easy way to fetch the SiacoinElement type needed to build + /// SiacoinInputs. + async fn utxo_from_txid(&self, txid: &TransactionId, vout_index: u32) -> Result { + let output_id = SiacoinOutputId::new(txid.clone(), vout_index); + + // fetch the Event via /api/events/{txid} + let event = self + .dispatcher(GetEventRequest { txid: txid.clone() }) + .await + .map_err(|e| UtxoFromTxidError::FetchEvent(e))?; + + // check that the fetched event is V2Transaction + let tx = match event.data { + EventDataWrapper::V2Transaction(tx) => tx, + _ => return Err(UtxoFromTxidError::EventVariant(event)), + }; + + // check that the output index is within bounds + if (tx.siacoin_outputs.len() as u32) <= vout_index { + return Err(UtxoFromTxidError::OutputIndexOutOfBounds { + txid: txid.clone(), + index: vout_index, + }); + } + + let output_address = tx.siacoin_outputs[vout_index as usize].address.clone(); + + // fetch unspent outputs of the address + let address_utxos = self + .get_unspent_outputs(&output_address, None, None) + .await + .map_err(|e| UtxoFromTxidError::FetchUtxos(e))?; + + // filter the utxos to find any matching the expected SiacoinOutputId + let filtered_utxos: Vec = address_utxos + .into_iter() + .filter(|element| element.state_element.id == output_id.clone().into()) + .collect(); + + // ensure only one utxo was found + match filtered_utxos.len() { + 1 => Ok(filtered_utxos[0].clone()), + 0 => Err(UtxoFromTxidError::NotFound { + txid: txid.clone(), + index: vout_index, + }), + _ => Err(UtxoFromTxidError::DuplicateUtxoFound { + txid: txid.clone(), + index: vout_index, + }), + } + } } From 1f250a8daf02d9483204c1eb2548274708b32254 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 20:14:17 -0400 Subject: [PATCH 162/233] make SpendPolicy::atomic_swap take references for convenience --- src/types/spend_policy.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 61cffe3..6ffdedf 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -136,18 +136,18 @@ impl SpendPolicy { } impl SpendPolicy { - pub fn atomic_swap(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { + pub fn atomic_swap(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { let policy_after = SpendPolicy::After(lock_time); - let policy_hash = SpendPolicy::Hash(hash); + let policy_hash = SpendPolicy::Hash(hash.clone()); let policy_success = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(alice), policy_hash], + of: vec![SpendPolicy::PublicKey(alice.clone()), policy_hash], }; let policy_refund = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(bob), policy_after], + of: vec![SpendPolicy::PublicKey(bob.clone()), policy_after], }; SpendPolicy::Threshold { @@ -156,7 +156,7 @@ impl SpendPolicy { } } - pub fn atomic_swap_success(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { + pub fn atomic_swap_success(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { match Self::atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[1] = of[1].opacify(); @@ -166,7 +166,7 @@ impl SpendPolicy { } } - pub fn atomic_swap_refund(alice: PublicKey, bob: PublicKey, lock_time: u64, hash: Hash256) -> Self { + pub fn atomic_swap_refund(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { match Self::atomic_swap(alice, bob, lock_time, hash) { SpendPolicy::Threshold { n, mut of } => { of[0] = of[0].opacify(); From 9d1a1baf53f4c26fa18851a4209e872c20a0e9ab Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 20:14:44 -0400 Subject: [PATCH 163/233] expose pub UtxoFromTxidError via client module --- src/transport/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client.rs b/src/transport/client.rs index 0024302..80486e8 100644 --- a/src/transport/client.rs +++ b/src/transport/client.rs @@ -10,7 +10,7 @@ use url::Url; #[cfg(target_arch = "wasm32")] pub mod wasm; mod helpers; -pub use helpers::{ApiClientHelpers, ApiClientHelpersError}; +pub use helpers::{ApiClientHelpers, ApiClientHelpersError, UtxoFromTxidError}; // FIXME remove these client specific error types #[cfg(not(target_arch = "wasm32"))] From e7e245d7870a217b7dd64c3f4d9c0de283daa805 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 20:15:02 -0400 Subject: [PATCH 164/233] add dev comment TODO re: V2TransactionBuilder helpers --- src/transport/client/helpers.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 0d799fe..a23d903 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -120,6 +120,10 @@ pub trait ApiClientHelpers: ApiClient { /// * `Ok(())` - The transaction builder has been successfully funded /// * `Err(ApiClientHelpersError)` - An error is returned if the available outputs cannot meet /// the required amount or a transport error is encountered. + // Alright TODO - move V2TransactionBuilder to a separate module then move this logic to a + // method of V2TransactionBuilder to allow chaining. It was included here because V2TransactionBuilder + // is currently inside the transaction module which is generally meant for consensnus related types. + // It would not be appropriate to include ApiClient-related code in transaction.rs async fn fund_tx_single_source( &self, tx_builder: &mut V2TransactionBuilder, From ffdf9e0f98a651d4905c611b356e2607e821e16d Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 20:47:01 -0400 Subject: [PATCH 165/233] fix lossy usize comparison --- src/transport/client/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index a23d903..8222eda 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -169,7 +169,7 @@ pub trait ApiClientHelpers: ApiClient { }; // check that the output index is within bounds - if (tx.siacoin_outputs.len() as u32) <= vout_index { + if tx.siacoin_outputs.len() <= (vout_index as usize) { return Err(UtxoFromTxidError::OutputIndexOutOfBounds { txid: txid.clone(), index: vout_index, From 639a7473131cafc9bd0c0982aae5320446d5746e Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 20:47:44 -0400 Subject: [PATCH 166/233] add V2TransactionBuilder::satisfy_atomic_swap_success --- src/types/transaction.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index b1d48b5..56e2bef 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1389,6 +1389,29 @@ impl V2TransactionBuilder { self } + pub fn satisfy_atomic_swap_success( + &mut self, + keypair: &Keypair, + secret: Preimage, + input_index: u32, + ) -> Result<&mut Self, SatisfyAtomicSwapSuccessError> { + let sig_hash = self.input_sig_hash(); + let sig = keypair.sign(&sig_hash.0); + + // check input_index exists prior to indexing into the vector + if self.siacoin_inputs.len() <= (input_index as usize) { + return Err(SatisfyAtomicSwapSuccessError::IndexOutOfBounds { + len: self.siacoin_inputs.len(), + index: input_index, + }); + } + + let htlc_input = &mut self.siacoin_inputs[input_index as usize]; + htlc_input.satisfied_policy.signatures.push(sig); + htlc_input.satisfied_policy.preimages.push(secret); + Ok(self) + } + pub fn build(&mut self) -> V2Transaction { let cloned = self.clone(); V2Transaction { @@ -1410,3 +1433,9 @@ impl V2TransactionBuilder { impl Default for V2TransactionBuilder { fn default() -> Self { V2TransactionBuilder::new() } } + +#[derive(Debug, Error)] +pub enum SatisfyAtomicSwapSuccessError { + #[error("satisfy_atomic_swap_success: provided index: {index} is out of bounds for inputs of length: {len}")] + IndexOutOfBounds { len: usize, index: u32 }, +} From 4c7e9a513f43a2155cebe7b10c7a7854c0e60713 Mon Sep 17 00:00:00 2001 From: Alright Date: Sat, 26 Oct 2024 21:17:03 -0400 Subject: [PATCH 167/233] set a Currency::DEFAULT_FEE const change Currency::DUST to 1 hasting --- src/types/transaction.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 56e2bef..3a26ceb 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -48,7 +48,11 @@ impl Currency { /// The minimum amount of currency for a transaction output // FIXME this is a placeholder value until testing is complete - pub const DUST: Currency = Currency(1_000_000); + pub const DUST: Currency = Currency(1); + + /// A default fee amount for transactions + /// FIXME This is a placeholder value until testing is complete + pub const DEFAULT_FEE: Currency = Currency(10000000000000000000); } // TODO does this also need to be able to deserialize from an integer? From e830900127e8ca94bb2c2f858c063880a8dcc730 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 15:39:32 -0400 Subject: [PATCH 168/233] refactor ApiClientHelpers error handling so allow single HelperError type --- src/transport/client/helpers.rs | 90 ++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 8222eda..7cffbb4 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,37 +1,57 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, GetAddressUtxosRequest, GetEventRequest}; -use crate::types::{Address, Currency, Event, EventDataWrapper, PublicKey, SiacoinElement, SiacoinOutputId, - SpendPolicy, TransactionId, V2TransactionBuilder}; +use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, + SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; use thiserror::Error; #[derive(Debug, Error)] -pub enum ApiClientHelpersError { - #[error( - "ApiClientHelpersError::SelectOutputs: insufficent amount, available: {available:?} required: {required:?}" - )] - SelectOutputs { available: Currency, required: Currency }, - #[error("ApiClientHelpersError::ApiClientError: {0}")] - ApiClientError(#[from] ApiClientError), +pub enum HelperError { + #[error("ApiClientHelpers::utxo_from_txid: {0}")] + UtxoFromTxid(#[from] UtxoFromTxidError), + #[error("ApiClientHelpers::get_transaction: {0}")] + GetTx(#[from] GetTransactionError), + #[error("ApiClientHelpers::select_unspent_outputs: {0}")] + SelectUtxos(#[from] SelectUtxosError), + #[error("ApiClientHelpers::get_event: failed to fetch event {0}")] + GetEvent(ApiClientError), } #[derive(Debug, Error)] pub enum UtxoFromTxidError { - #[error("utxo_from_txid: failed to fetch event {0}")] + #[error("ApiClientHelpers::utxo_from_txid: failed to fetch event {0}")] FetchEvent(ApiClientError), - #[error("utxo_from_txid: invalid event variant {0:?}")] + #[error("ApiClientHelpers::utxo_from_txid: invalid event variant {0:?}")] EventVariant(Event), - #[error("utxo_from_txid: output index out of bounds txid: {txid:?} index: {index:?}")] + #[error("ApiClientHelpers::utxo_from_txid: output index out of bounds txid: {txid} index: {index}")] OutputIndexOutOfBounds { txid: TransactionId, index: u32 }, - #[error("utxo_from_txid: get_unspent_outputs helper failed {0}")] + #[error("ApiClientHelpers::utxo_from_txid: get_unspent_outputs helper failed {0}")] FetchUtxos(ApiClientError), - #[error("utxo_from_txid: output not found txid: {txid:?} index: {index:?}")] + #[error("ApiClientHelpers::utxo_from_txid: output not found txid: {txid} index: {index}")] NotFound { txid: TransactionId, index: u32 }, - #[error("utxo_from_txid: found duplicate utxo txid: {txid:?} index: {index:?}")] + #[error("ApiClientHelpers::utxo_from_txid: found duplicate utxo txid: {txid} index: {index}")] DuplicateUtxoFound { txid: TransactionId, index: u32 }, } +#[derive(Debug, Error)] +pub enum GetTransactionError { + #[error("ApiClientHelpers::get_transaction: failed to fetch event {0}")] + FetchEvent(#[from] ApiClientError), + #[error("ApiClientHelpers::get_transaction: unexpected variant error {0:?}")] + EventVariant(EventDataWrapper), +} + +#[derive(Debug, Error)] +pub enum SelectUtxosError { + #[error( + "ApiClientHelpers::select_unspent_outputs: insufficent funds, available: {available:?} required: {required:?}" + )] + Funding { available: Currency, required: Currency }, + #[error("ApiClientHelpers::select_unspent_outputs: failed to fetch UTXOs {0}")] + FetchUtxos(#[from] ApiClientError), +} + /// Helper methods for the ApiClient trait /// These generally provide higher level functionality than the base ApiClient trait /// This crate is focused on catering to the Komodo Defi Framework integration @@ -77,8 +97,11 @@ pub trait ApiClientHelpers: ApiClient { &self, address: &Address, total_amount: Currency, - ) -> Result<(Vec, Currency), ApiClientHelpersError> { - let mut unspent_outputs = self.get_unspent_outputs(address, None, None).await?; + ) -> Result<(Vec, Currency), HelperError> { + let mut unspent_outputs = self + .get_unspent_outputs(address, None, None) + .await + .map_err(SelectUtxosError::FetchUtxos)?; // Sort outputs from largest to smallest unspent_outputs.sort_by(|a, b| b.siacoin_output.value.0.cmp(&a.siacoin_output.value.0)); @@ -97,10 +120,10 @@ pub trait ApiClientHelpers: ApiClient { } if selected_amount < *total_amount { - return Err(ApiClientHelpersError::SelectOutputs { + return Err(SelectUtxosError::Funding { available: selected_amount.into(), required: total_amount.into(), - }); + })?; } let change = selected_amount as u128 - *total_amount; @@ -128,7 +151,7 @@ pub trait ApiClientHelpers: ApiClient { &self, tx_builder: &mut V2TransactionBuilder, public_key: &PublicKey, - ) -> Result<(), ApiClientHelpersError> { + ) -> Result<(), HelperError> { let address = public_key.address(); let outputs_total: Currency = tx_builder.siacoin_outputs.iter().map(|output| output.value).sum(); @@ -153,7 +176,7 @@ pub trait ApiClientHelpers: ApiClient { /// Fetches a SiacoinElement(a UTXO) from a TransactionId and Index /// Walletd doesn't currently offer an easy way to fetch the SiacoinElement type needed to build /// SiacoinInputs. - async fn utxo_from_txid(&self, txid: &TransactionId, vout_index: u32) -> Result { + async fn utxo_from_txid(&self, txid: &TransactionId, vout_index: u32) -> Result { let output_id = SiacoinOutputId::new(txid.clone(), vout_index); // fetch the Event via /api/events/{txid} @@ -165,7 +188,7 @@ pub trait ApiClientHelpers: ApiClient { // check that the fetched event is V2Transaction let tx = match event.data { EventDataWrapper::V2Transaction(tx) => tx, - _ => return Err(UtxoFromTxidError::EventVariant(event)), + _ => return Err(UtxoFromTxidError::EventVariant(event))?, }; // check that the output index is within bounds @@ -173,7 +196,7 @@ pub trait ApiClientHelpers: ApiClient { return Err(UtxoFromTxidError::OutputIndexOutOfBounds { txid: txid.clone(), index: vout_index, - }); + })?; } let output_address = tx.siacoin_outputs[vout_index as usize].address.clone(); @@ -196,11 +219,28 @@ pub trait ApiClientHelpers: ApiClient { 0 => Err(UtxoFromTxidError::NotFound { txid: txid.clone(), index: vout_index, - }), + })?, _ => Err(UtxoFromTxidError::DuplicateUtxoFound { txid: txid.clone(), index: vout_index, - }), + })?, + } + } + + async fn get_event(&self, event_id: &Hash256) -> Result { + self.dispatcher(GetEventRequest { txid: event_id.clone() }) + .await + .map_err(HelperError::GetEvent) + } + + async fn get_transaction(&self, txid: &TransactionId) -> Result { + let event = self + .dispatcher(GetEventRequest { txid: txid.clone() }) + .await + .map_err(GetTransactionError::FetchEvent)?; + match event.data { + EventDataWrapper::V2Transaction(tx) => Ok(tx), + wrong_variant => Err(GetTransactionError::EventVariant(wrong_variant))?, } } } From b291bad246f5b2ed6637cac0970e12d1cc4ed36c Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 15:41:01 -0400 Subject: [PATCH 169/233] remove UtxoFromTxidError pub export from helpers module. UtxoFromTxidError is now wrapped in HelperError --- src/transport/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client.rs b/src/transport/client.rs index 80486e8..741c003 100644 --- a/src/transport/client.rs +++ b/src/transport/client.rs @@ -10,7 +10,7 @@ use url::Url; #[cfg(target_arch = "wasm32")] pub mod wasm; mod helpers; -pub use helpers::{ApiClientHelpers, ApiClientHelpersError, UtxoFromTxidError}; +pub use helpers::{ApiClientHelpers, HelperError}; // FIXME remove these client specific error types #[cfg(not(target_arch = "wasm32"))] From dc17056a56ccd34f360e634dde2cb2b1ed452b35 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 18:12:46 -0400 Subject: [PATCH 170/233] fix tests compilation after SpendPolicy::atomic_swap method arguments were changed to take refs --- src/tests/spend_policy.rs | 2 +- src/tests/transaction.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 55b269a..bc9d704 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -89,7 +89,7 @@ mod test { .unwrap(); let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let spend_policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let spend_policy = SpendPolicy::atomic_swap_success(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let j = json!( { diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index e18c101..070f191 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -322,7 +322,7 @@ mod test { let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let policy = SpendPolicy::atomic_swap_success(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let policy = SpendPolicy::atomic_swap_success(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); @@ -352,7 +352,7 @@ mod test { let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); - let policy = SpendPolicy::atomic_swap_refund(alice_pubkey, bob_pubkey, 77777777, secret_hash); + let policy = SpendPolicy::atomic_swap_refund(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let signature = Signature::from_bytes( &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); From 283ea9e65fdefaaf8d8440fd4d7be1f3c346d27f Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 18:36:42 -0400 Subject: [PATCH 171/233] remove incomplete atomic_swap module; move atomic swaps comment block to spend_policy module --- src/types/atomic_swap.rs | 785 -------------------------------------- src/types/spend_policy.rs | 80 ++++ 2 files changed, 80 insertions(+), 785 deletions(-) delete mode 100644 src/types/atomic_swap.rs diff --git a/src/types/atomic_swap.rs b/src/types/atomic_swap.rs deleted file mode 100644 index 7fdf13d..0000000 --- a/src/types/atomic_swap.rs +++ /dev/null @@ -1,785 +0,0 @@ -use crate::types::{Address, SatisfiedPolicy, SpendPolicy}; -use thiserror::Error; - -/* -The full representation of the atomic swap contract is as follows: - SpendPolicy::Threshold { - n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::Hash(), - SpendPolicy::PublicKey() - ] - }, - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::After(), - SpendPolicy::PublicKey() - ] - }, - ] - } - -In English, the above specifies that: - - Alice can spend the UTXO if: - - Alice provides the preimage of the SHA256 hash specified in the UTXO (the secret) - - Alice provides a valid signature - - Bob can spend the UTXO if: - - the current time is greater than the specified timestamp - - Bob provides a valid signature - -To lock funds in such a contract, we generate the address(see SpendPolicy::address) of the above SpendPolicy and use this Address in a transaction output. - -The resulting UTXO can then be spent by either Alice or Bob by meeting the conditions specified above. - -It is only neccesary to reveal the path that will be satisfied. The other path will be opacified(see SpendPolicy::opacify) and replaced with SpendPolicy::Opaque(). - -Alice can spend the UTXO by providing a signature, the secret and revealing the relevant path within the full SpendPolicy. - -Alice can construct the following SatisfiedPolicy to spend the UTXO: - -SatisfiedPolicy { - policy: SpendPolicy::Threshold { - n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::Hash(), - SpendPolicy::PublicKey() - ] - }, - SpendPolicy::Opaque(), - ] - }, - signatures: vec![], - preimages: vec![] -} - -Similarly, Bob can spend the UTXO with the following SatisfiedPolicy assuming he waits until the timestamp has passed: - -SatisfiedPolicy { - policy: SpendPolicy::Threshold { - n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, - of: vec![ - SpendPolicy::After(), - SpendPolicy::PublicKey() - ] - }, - SpendPolicy::Opaque(), - ] - }, - signatures: vec![], - preimages: vec![] -} - -*/ - -/// SpendPolicy is a recursive structure that represents the conditions required to spend a UTXO. -/// This trait enables having wrapper types that enforce a specific structure. -/// Each specific structure of SpendPolicy or SatisfiedPolicy should implement this trait. -/// The stored inner type must not be exposed via pub -/// otherwise a consumer can initialize with invalid data. ie, AtomicSwap(invalid_policy) -trait IsValidatedPolicy { - type Error; - type Inner; - - // allow reference to inner policy because it is not public - fn inner(&self) -> &Self::Inner; - - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error>; -} - -#[derive(Debug, Error)] -pub enum AtomicSwapError { - #[error("invalid atomic swap, invalid hash component: {}", .0)] - InvalidHashComponent(ComponentError), - #[error("invalid atomic swap, invalid time component: {}", .0)] - InvalidTimeComponent(ComponentError), - #[error("invalid atomic swap, wrong n:{} policy: {:?}", n, policy)] - InvalidN { policy: SpendPolicy, n: u8 }, - #[error("invalid atomic swap, wrong m:{} policy: {:?}", m, policy)] - InvalidM { policy: SpendPolicy, m: usize }, - #[error("invalid atomic swap, not a threshold: {:?}", .0)] - InvalidSpendPolicyVariant(SpendPolicy), -} - -/// Represents an atomic swap contract. Structure is documented above. -/// SpendPolicy:address is used to generate the address of the contract. -/// Funds can then be locked in the contract by creating a transaction output with this address. -/// This is used only to create outputs, never inputs. -/// The order of the SpendPolicys within a SpendPolicy::Threshold have no meaningful impact on logic, but we enforce a strict structure for simplicity. -#[derive(Debug)] -pub struct AtomicSwap(SpendPolicy); - -impl AtomicSwap { - pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } - - pub fn address(&self) -> Address { self.inner().address() } - - pub fn opacify(&self) -> SpendPolicy { self.inner().opacify() } -} - -impl IsValidatedPolicy for AtomicSwap { - type Error = AtomicSwapError; - type Inner = SpendPolicy; - - fn inner(&self) -> &Self::Inner { &self.0 } - - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { - match policy { - SpendPolicy::Threshold { n: 1, of } if of.len() == 2 => { - HashLockPath::is_valid(&of[0]).map_err(AtomicSwapError::InvalidHashComponent)?; - TimeLockPath::is_valid(&of[1]).map_err(AtomicSwapError::InvalidTimeComponent)?; - Ok(()) - }, - SpendPolicy::Threshold { n: 1, of } => Err(AtomicSwapError::InvalidM { - policy: policy.clone(), - m: of.len(), - }), - SpendPolicy::Threshold { n, of: _ } => Err(AtomicSwapError::InvalidN { - policy: policy.clone(), - n: n.clone(), - }), - _ => Err(AtomicSwapError::InvalidSpendPolicyVariant(policy.clone())), - } - } -} - -#[derive(Debug, Error)] -pub enum ComponentError { - #[error("invalid hash lock component, hash lock path: {:?}", .0)] - HashLockInvalidThresholdStructure(SpendPolicy), - #[error("invalid hash lock component, not a threshold: {:?}", .0)] - HashLockInvalidSpendPolicyVariant(SpendPolicy), - #[error("invalid hash lock component, wrong n:{} policy: {:?}", n, policy)] - HashLockInvalidN { policy: SpendPolicy, n: u8 }, - #[error("invalid hash lock component, wrong m:{} policy: {:?}", m, policy)] - HashLockInvalidM { policy: SpendPolicy, m: usize }, - #[error("invalid time lock component, time lock path: {:?}", .0)] - TimeLockInvalidThresholdStructure(SpendPolicy), - #[error("invalid time lock component, not a threshold: {:?}", .0)] - TimeLockInvalidSpendPolicyVariant(SpendPolicy), - #[error("invalid time lock component, wrong n:{} policy: {:?}", n, policy)] - TimeLockInvalidN { policy: SpendPolicy, n: u8 }, - #[error("invalid time lock component, wrong m:{} policy: {:?}", m, policy)] - TimeLockInvalidM { policy: SpendPolicy, m: usize }, - #[error("invalid component, not SpendPolicy::Opaque: {:?}", .0)] - OpaqueInvalidSpendPolicyVariant(SpendPolicy), -} - -/// The hash locked threshold path of the atomic swap contract. -/// structure: -/// SpendPolicy::Threshold { -/// n: 2, -/// of: vec![ -/// SpendPolicy::Hash(sha256(secret)), -/// SpendPolicy::PublicKey(public_key) -/// ] -/// } -/// fulfillment conditions: -/// - signature from participant's public key -/// - sha256(secret) == hash -/// - length(secret) == 32 -#[derive(Debug)] -pub struct HashLockPath(SpendPolicy); - -impl HashLockPath { - pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } -} - -impl IsValidatedPolicy for HashLockPath { - type Error = ComponentError; - type Inner = SpendPolicy; - - fn inner(&self) -> &Self::Inner { &self.0 } - - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { - match policy { - SpendPolicy::Threshold { n: 2, of } if of.len() == 2 => match of.as_slice() { - [SpendPolicy::Hash(_), SpendPolicy::PublicKey(_)] => Ok(()), - _ => Err(ComponentError::HashLockInvalidThresholdStructure(policy.clone())), - }, - SpendPolicy::Threshold { n: 2, of } => Err(ComponentError::HashLockInvalidM { - policy: policy.clone(), - m: of.len(), - }), - SpendPolicy::Threshold { n, of: _ } => Err(ComponentError::HashLockInvalidN { - policy: policy.clone(), - n: n.clone(), - }), - _ => Err(ComponentError::HashLockInvalidSpendPolicyVariant(policy.clone())), - } - } -} - -/// The time based threshold path of the atomic swap contract. -/// structure: -/// SpendPolicy::Threshold { -/// n: 2, -/// of: vec![ -/// SpendPolicy::After(timestamp), -/// SpendPolicy::PublicKey(public_key) -/// ] -/// } -/// fulfillment conditions: -/// - signature from participant's public key -/// - timestamp has passed -#[derive(Debug)] -pub struct TimeLockPath(SpendPolicy); - -impl TimeLockPath { - pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } -} - -impl IsValidatedPolicy for TimeLockPath { - type Error = ComponentError; - type Inner = SpendPolicy; - - fn inner(&self) -> &Self::Inner { &self.0 } - - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { - match policy { - SpendPolicy::Threshold { n: 2, of } if of.len() == 2 => match of.as_slice() { - [SpendPolicy::After(_), SpendPolicy::PublicKey(_)] => Ok(()), - _ => Err(ComponentError::TimeLockInvalidThresholdStructure(policy.clone())), - }, - SpendPolicy::Threshold { n: 2, of } => Err(ComponentError::TimeLockInvalidM { - policy: policy.clone(), - m: of.len(), - }), - SpendPolicy::Threshold { n, of: _ } => Err(ComponentError::TimeLockInvalidN { - policy: policy.clone(), - n: n.clone(), - }), - _ => Err(ComponentError::TimeLockInvalidSpendPolicyVariant(policy.clone())), - } - } -} - -#[derive(Debug)] -pub struct Opaque(SpendPolicy); - -impl Opaque { - pub fn new(policy: SpendPolicy) -> Result { Self::is_valid(&policy).map(|_| Self(policy)) } -} - -impl IsValidatedPolicy for Opaque { - type Error = ComponentError; - type Inner = SpendPolicy; - - fn inner(&self) -> &Self::Inner { &self.0 } - - fn is_valid(policy: &Self::Inner) -> Result<(), Self::Error> { - match policy { - SpendPolicy::Opaque(_) => Ok(()), - _ => Err(ComponentError::OpaqueInvalidSpendPolicyVariant(policy.clone())), - } - } -} - -/// Represents the SatisfiedPolicy for the success path of an atomic swap. -/// Used within the input that spends the locked UTXO. -/// structure: -/// SatisfiedPolicy { -/// policy: SpendPolicy::Threshold { -/// n: 1, -/// of: vec![ -/// SpendPolicy::Threshold { -/// n: 2, -/// of: vec![ -/// SpendPolicy::Hash(sha256(secret)), -/// SpendPolicy::PublicKey(public_key) -/// ] -/// }, -/// SpendPolicy::Opaque(), -/// ] -/// }, -/// signatures: vec![signature], -/// preimages: vec![secret] -/// } -#[derive(Debug, Clone, PartialEq)] -pub struct AtomicSwapSuccess(SatisfiedPolicy); - -#[derive(Debug, Error)] -enum AtomicSwapSuccessError { - #[error( - "invalid atomic swap success: {:?}, invalid signatures amount: {}", - satisfied_policy, - amount - )] - InvalidSignaturesAmount { - satisfied_policy: SatisfiedPolicy, - amount: usize, - }, - #[error( - "invalid atomic swap success: {:?}, invalid preimages amount: {}", - satisfied_policy, - amount - )] - InvalidPreimagesAmount { - satisfied_policy: SatisfiedPolicy, - amount: usize, - }, - #[error("invalid atomic swap success: {:?}, invalid policy variant", satisfied_policy)] - InvalidSpendPolicyVariant { satisfied_policy: SatisfiedPolicy }, - // #[error("invalid atomic swap success: {0}, atomic swap error")] - // AtomicSwapError(AtomicSwapError), - #[error("invalid atomic swap success, TODO remove this placeholder")] - PlaceHolder, -} - -impl IsValidatedPolicy for AtomicSwapSuccess { - type Error = AtomicSwapSuccessError; - type Inner = SatisfiedPolicy; - - fn inner(&self) -> &Self::Inner { &self.0 } - - fn is_valid(satisfied_policy: &Self::Inner) -> Result<(), Self::Error> { - if satisfied_policy.signatures.len() != 1 { - return Err(AtomicSwapSuccessError::InvalidSignaturesAmount { - satisfied_policy: satisfied_policy.clone(), - amount: satisfied_policy.signatures.len(), - }); - } - - if satisfied_policy.preimages.len() != 1 { - return Err(AtomicSwapSuccessError::InvalidPreimagesAmount { - satisfied_policy: satisfied_policy.clone(), - amount: satisfied_policy.preimages.len(), - }); - } - - match &satisfied_policy.policy { - SpendPolicy::Threshold { n: 1, of } if of.len() == 2 => { - HashLockPath::is_valid(&of[0]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; - Opaque::is_valid(&of[1]).map_err(|_| AtomicSwapSuccessError::PlaceHolder)?; - Ok(()) - }, - _ => Err(AtomicSwapSuccessError::InvalidSpendPolicyVariant { - satisfied_policy: satisfied_policy.clone(), - }), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::types::{Hash256, PublicKey}; - - fn pubkey0() -> PublicKey { - PublicKey::from_bytes(&hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap()) - .unwrap() - } - - fn pubkey1() -> PublicKey { - PublicKey::from_bytes(&hex::decode("06C87838297B7BB16AB23946C99DFDF77FF834E35DB07D71E9B1D2B01A11E96D").unwrap()) - .unwrap() - } - - fn valid_atomic_swap_spend_policy() -> SpendPolicy { - SpendPolicy::Threshold { - n: 1, - of: vec![ - SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::Hash(Hash256::default()), SpendPolicy::PublicKey(pubkey0())], - }, - SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::After(0), SpendPolicy::PublicKey(pubkey1())], - }, - ], - } - } - - fn valid_component_hash_lock() -> SpendPolicy { - SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::Hash(Hash256::default()), SpendPolicy::PublicKey(pubkey0())], - } - } - - fn valid_component_time_lock() -> SpendPolicy { - SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::After(0), SpendPolicy::PublicKey(pubkey1())], - } - } - - #[test] - fn test_atomic_swap_contract_valid() { AtomicSwap::new(valid_atomic_swap_spend_policy()).unwrap(); } - - #[test] - fn test_atomic_swap_contract_invalid_hash_lock_path() { - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![SpendPolicy::PublicKey(pubkey0()), valid_component_time_lock()], - }; - - match AtomicSwap::new(policy.clone()).unwrap_err() { - AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidSpendPolicyVariant(_)) => (), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_time_lock_path() { - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![valid_component_hash_lock(), SpendPolicy::PublicKey(pubkey0())], - }; - - match AtomicSwap::new(policy.clone()).unwrap_err() { - AtomicSwapError::InvalidTimeComponent(ComponentError::TimeLockInvalidSpendPolicyVariant(_)) => (), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_components_wrong_order() { - let policy = SpendPolicy::Threshold { - n: 1, - of: vec![valid_component_time_lock(), valid_component_hash_lock()], - }; - - match AtomicSwap::new(policy.clone()).unwrap_err() { - AtomicSwapError::InvalidHashComponent(ComponentError::HashLockInvalidThresholdStructure(_)) => (), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_components_too_many() { - let mut policy = valid_atomic_swap_spend_policy(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - of.push(SpendPolicy::PublicKey(pubkey0())); - }, - _ => unreachable!(), - } - - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidM { policy: _, m }) => { - assert_eq!(m, 3); - }, - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_components_missing_time_lock_path() { - let mut policy = valid_atomic_swap_spend_policy(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - let _ = of.pop().unwrap(); - }, - _ => unreachable!(), - } - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidM { policy: _, m }) => { - assert_eq!(m, 1); - }, - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_components_missing_hash_lock_path() { - let mut policy = valid_atomic_swap_spend_policy(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - let _ = of.remove(0); - }, - _ => unreachable!(), - } - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidM { policy: _, m }) => { - assert_eq!(m, 1); - }, - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_components_missing_both_paths() { - let mut policy = valid_atomic_swap_spend_policy(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - *of = vec![]; - }, - _ => unreachable!(), - } - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidM { policy: _, m }) => { - assert_eq!(m, 0); - }, - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_n() { - let mut policy = valid_atomic_swap_spend_policy(); - match &mut policy { - SpendPolicy::Threshold { n, .. } => *n = 10, - _ => unreachable!(), - } - - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidN { policy: _, n }) => { - assert_eq!(n, 10); - }, - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_contract_invalid_policy_variant() { - let policy = SpendPolicy::PublicKey(pubkey0()); - - match AtomicSwap::new(policy.clone()) { - Err(AtomicSwapError::InvalidSpendPolicyVariant { .. }) => (), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_valid() { HashLockPath::new(valid_component_hash_lock()).unwrap(); } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_threshold_structure() { - let policy = SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::PublicKey(pubkey0()), SpendPolicy::PublicKey(pubkey0())], - }; - - match HashLockPath::new(policy.clone()).unwrap_err() { - ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_wrong_order() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - of.reverse(); - }, - _ => unreachable!(), - } - - match HashLockPath::new(policy.clone()).unwrap_err() { - ComponentError::HashLockInvalidThresholdStructure(p) => assert_eq!(p, policy), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_too_many() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - of.push(SpendPolicy::PublicKey(pubkey0())); - }, - _ => unreachable!(), - } - - match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 3), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_missing_public_key() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::Hash(Hash256::default())], - _ => unreachable!(), - } - - match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 1), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_missing_hash() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::PublicKey(pubkey0())], - _ => unreachable!(), - } - - match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 1), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_empty_threshold() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![], - _ => unreachable!(), - } - - match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidM { policy: _, m } => assert_eq!(m, 0), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_n() { - let mut policy = valid_component_hash_lock(); - match &mut policy { - SpendPolicy::Threshold { n, of: _ } => { - *n = 10; - }, - _ => unreachable!(), - } - - match HashLockPath::new(policy).unwrap_err() { - ComponentError::HashLockInvalidN { policy: _, n } => assert_eq!(n, 10), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_hash_lock_invalid_policy_variant() { - let policy = SpendPolicy::PublicKey(pubkey0()); - - match HashLockPath::new(policy.clone()).unwrap_err() { - ComponentError::HashLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_valid() { TimeLockPath::new(valid_component_time_lock()).unwrap(); } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_threshold_structure() { - let policy = SpendPolicy::Threshold { - n: 2, - of: vec![SpendPolicy::PublicKey(pubkey0()), SpendPolicy::PublicKey(pubkey0())], - }; - - match TimeLockPath::new(policy.clone()).unwrap_err() { - ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_wrong_order() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - of.reverse(); - }, - _ => unreachable!(), - } - - match TimeLockPath::new(policy.clone()).unwrap_err() { - ComponentError::TimeLockInvalidThresholdStructure(p) => assert_eq!(p, policy), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_too_many() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => { - of.push(SpendPolicy::PublicKey(pubkey0())); - }, - _ => unreachable!(), - } - - match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 3), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_missing_public_key() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::After(0)], - _ => unreachable!(), - } - - match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 1), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_missing_time() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![SpendPolicy::PublicKey(pubkey1())], - _ => unreachable!(), - } - - match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 1), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_empty_threshold() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n: _, of } => *of = vec![], - _ => unreachable!(), - } - - match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidM { policy: _, m } => assert_eq!(m, 0), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_n() { - let mut policy = valid_component_time_lock(); - match &mut policy { - SpendPolicy::Threshold { n, of: _ } => { - *n = 10; - }, - _ => unreachable!(), - } - - match TimeLockPath::new(policy).unwrap_err() { - ComponentError::TimeLockInvalidN { policy: _, n } => assert_eq!(n, 10), - _ => panic!(), - } - } - - #[test] - fn test_atomic_swap_component_time_lock_invalid_policy_variant() { - let policy = SpendPolicy::PublicKey(pubkey0()); - - match TimeLockPath::new(policy.clone()).unwrap_err() { - ComponentError::TimeLockInvalidSpendPolicyVariant(p) => assert_eq!(p, policy), - _ => panic!(), - } - } -} diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 6ffdedf..96d4dda 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -12,6 +12,86 @@ use std::str::FromStr; const POLICY_VERSION: u8 = 1u8; +/* +The full representation of the atomic swap contract is as follows: + SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(), + SpendPolicy::PublicKey() + ] + }, + ] + } + +In English, the above specifies that: + - Alice can spend the UTXO if: + - Alice provides the preimage of the SHA256 hash specified in the UTXO (the secret) + - Alice provides a valid signature + - Bob can spend the UTXO if: + - the current time is greater than the specified timestamp + - Bob provides a valid signature + +To lock funds in such a contract, we generate the address(see SpendPolicy::address) of the above SpendPolicy and use this Address in a transaction output. + +The resulting UTXO can then be spent by either Alice or Bob by meeting the conditions specified above. + +It is only neccesary to reveal the path that will be satisfied. The other path will be opacified(see SpendPolicy::opacify) and replaced with SpendPolicy::Opaque(). + +Alice can spend the UTXO by providing a signature, the secret and revealing the relevant path within the full SpendPolicy. + +Alice can construct the following SatisfiedPolicy to spend the UTXO: + +SatisfiedPolicy { + policy: SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::Hash(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Opaque(), + ] + }, + signatures: vec![], + preimages: vec![] +} + +Similarly, Bob can spend the UTXO with the following SatisfiedPolicy assuming he waits until the timestamp has passed: + +SatisfiedPolicy { + policy: SpendPolicy::Threshold { + n: 1, + of: vec![ + SpendPolicy::Threshold { + n: 2, + of: vec![ + SpendPolicy::After(), + SpendPolicy::PublicKey() + ] + }, + SpendPolicy::Opaque(), + ] + }, + signatures: vec![], + preimages: vec![] +} + +*/ + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(tag = "type", content = "policy")] pub enum SpendPolicy { From 4d2482133ee1771d82c1eda4624d53fc620482ce Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 18:37:51 -0400 Subject: [PATCH 172/233] remove atomic_swap module from types module --- src/types.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/types.rs b/src/types.rs index 6d389b5..7531038 100644 --- a/src/types.rs +++ b/src/types.rs @@ -28,9 +28,6 @@ pub use transaction::*; mod specifier; pub use specifier::*; -mod atomic_swap; -pub use atomic_swap::*; - const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; From 7887e9108f8b5d474b7e1cc9f3b98d2c30c35fc9 Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 18:39:05 -0400 Subject: [PATCH 173/233] use Currency directly for arithmetic in ApiClientHelpers::select_unspent_outputs instead of converting to/from u128 --- src/transport/client/helpers.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 7cffbb4..27f1e62 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -107,27 +107,27 @@ pub trait ApiClientHelpers: ApiClient { unspent_outputs.sort_by(|a, b| b.siacoin_output.value.0.cmp(&a.siacoin_output.value.0)); let mut selected = Vec::new(); - let mut selected_amount = 0; + let mut selected_amount = Currency::ZERO; // Select outputs until the total amount is reached for output in unspent_outputs { - selected_amount += *output.siacoin_output.value; + selected_amount += output.siacoin_output.value; selected.push(output); - if selected_amount >= *total_amount { + if selected_amount >= total_amount { break; } } - if selected_amount < *total_amount { + if selected_amount < total_amount { return Err(SelectUtxosError::Funding { - available: selected_amount.into(), - required: total_amount.into(), + available: selected_amount, + required: total_amount, })?; } - let change = selected_amount as u128 - *total_amount; + let change = selected_amount - total_amount; - Ok((selected, change.into())) + Ok((selected, change)) } /// Fund a transaction with utxos from the given address. From ce321bb1b365aed6f8e3bdbf52af25d9d1c9dfbe Mon Sep 17 00:00:00 2001 From: Alright Date: Sun, 27 Oct 2024 18:39:30 -0400 Subject: [PATCH 174/233] fix various cargo clippy warnings/errors --- src/transport/client/helpers.rs | 4 ++-- src/types.rs | 2 +- src/types/keypair.rs | 2 +- src/types/signature.rs | 2 +- src/types/transaction.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 27f1e62..47c96f9 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -183,7 +183,7 @@ pub trait ApiClientHelpers: ApiClient { let event = self .dispatcher(GetEventRequest { txid: txid.clone() }) .await - .map_err(|e| UtxoFromTxidError::FetchEvent(e))?; + .map_err(UtxoFromTxidError::FetchEvent)?; // check that the fetched event is V2Transaction let tx = match event.data { @@ -205,7 +205,7 @@ pub trait ApiClientHelpers: ApiClient { let address_utxos = self .get_unspent_outputs(&output_address, None, None) .await - .map_err(|e| UtxoFromTxidError::FetchUtxos(e))?; + .map_err(UtxoFromTxidError::FetchUtxos)?; // filter the utxos to find any matching the expected SiacoinOutputId let filtered_utxos: Vec = address_utxos diff --git a/src/types.rs b/src/types.rs index 7531038..70f1b09 100644 --- a/src/types.rs +++ b/src/types.rs @@ -314,7 +314,7 @@ impl<'de> Deserialize<'de> for Event { }?; Ok(Event { - id: helper.id.into(), + id: helper.id, index: helper.index, timestamp: helper.timestamp, maturity_height: helper.maturity_height, diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 0789e4e..812eb54 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -39,7 +39,7 @@ impl Signer for Keypair { /// Sign a message with this keypair's secret key. fn try_sign(&self, message: &[u8]) -> Result { let expanded: ExpandedSecretKey = (&self.private.0).into(); - Ok(Signature::new(expanded.sign(&message, &self.public.0))) + Ok(Signature::new(expanded.sign(message, &self.public.0))) } } diff --git a/src/types/signature.rs b/src/types/signature.rs index 3e6bf35..61ea86a 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -73,7 +73,7 @@ impl Signature { } pub fn verify(&self, message: &[u8], public_key: &PublicKey) -> Result<(), SignatureError> { - public_key.verify(message, &self) + public_key.verify(message, self) } } diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 3a26ceb..7af334c 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -192,7 +192,7 @@ impl Encodable for SatisfiedPolicy { }, SpendPolicy::Hash(_) => { if *prei < sp.preimages.len() { - encoder.write_len_prefixed_bytes(&sp.preimages[*prei].0.to_vec()); + encoder.write_len_prefixed_bytes(&sp.preimages[*prei].0); *prei += 1; } else { // Sia Go code panics here but our code assumes encoding will always be successful From 1af41ae378bc8a4105a327712722242b92c63fcb Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 28 Oct 2024 13:00:24 -0400 Subject: [PATCH 175/233] fix spend_policy dev comments re: atomic swap SpendPolicy --- src/types/spend_policy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 96d4dda..8ab6c3c 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -76,18 +76,18 @@ SatisfiedPolicy { policy: SpendPolicy::Threshold { n: 1, of: vec![ + SpendPolicy::Opaque(), SpendPolicy::Threshold { n: 2, of: vec![ SpendPolicy::After(), SpendPolicy::PublicKey() ] - }, - SpendPolicy::Opaque(), + } ] }, signatures: vec![], - preimages: vec![] + preimages: vec![] } */ From 3b4abe1830a509243bbf9cdf936e5ffc2a760c0d Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 28 Oct 2024 15:23:21 -0400 Subject: [PATCH 176/233] Make SpendPolicy atomic swap helpers more verbose - add doc comment regarding arguments --- src/types/spend_policy.rs | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/types/spend_policy.rs b/src/types/spend_policy.rs index 8ab6c3c..3e0a8a8 100644 --- a/src/types/spend_policy.rs +++ b/src/types/spend_policy.rs @@ -216,18 +216,30 @@ impl SpendPolicy { } impl SpendPolicy { - pub fn atomic_swap(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { + /// Create a HTLC SpendPolicy. + /// Arguments: + /// - success_public_key: the public key that is able to claim the funds immediately by providing + /// the preimage of `secret_hash` + /// - refund_public_key: the public key that is able to claim the funds after `lock_time` + /// - lock_time: the timestamp after which `refund_public_key` can claim the funds + /// - secret_hash: the sha256 hash of the secret + pub fn atomic_swap( + success_public_key: &PublicKey, + refund_public_key: &PublicKey, + lock_time: u64, + secret_hash: &Hash256, + ) -> Self { let policy_after = SpendPolicy::After(lock_time); - let policy_hash = SpendPolicy::Hash(hash.clone()); + let policy_hash = SpendPolicy::Hash(secret_hash.clone()); let policy_success = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(alice.clone()), policy_hash], + of: vec![SpendPolicy::PublicKey(success_public_key.clone()), policy_hash], }; let policy_refund = SpendPolicy::Threshold { n: 2, - of: vec![SpendPolicy::PublicKey(bob.clone()), policy_after], + of: vec![SpendPolicy::PublicKey(refund_public_key.clone()), policy_after], }; SpendPolicy::Threshold { @@ -236,8 +248,13 @@ impl SpendPolicy { } } - pub fn atomic_swap_success(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { - match Self::atomic_swap(alice, bob, lock_time, hash) { + pub fn atomic_swap_success( + success_public_key: &PublicKey, + refund_public_key: &PublicKey, + lock_time: u64, + secret_hash: &Hash256, + ) -> Self { + match Self::atomic_swap(success_public_key, refund_public_key, lock_time, secret_hash) { SpendPolicy::Threshold { n, mut of } => { of[1] = of[1].opacify(); SpendPolicy::Threshold { n, of } @@ -246,8 +263,13 @@ impl SpendPolicy { } } - pub fn atomic_swap_refund(alice: &PublicKey, bob: &PublicKey, lock_time: u64, hash: &Hash256) -> Self { - match Self::atomic_swap(alice, bob, lock_time, hash) { + pub fn atomic_swap_refund( + success_public_key: &PublicKey, + refund_public_key: &PublicKey, + lock_time: u64, + secret_hash: &Hash256, + ) -> Self { + match Self::atomic_swap(success_public_key, refund_public_key, lock_time, secret_hash) { SpendPolicy::Threshold { n, mut of } => { of[0] = of[0].opacify(); SpendPolicy::Threshold { n, of } From 938b96906caaa4708acaa0b5ea6ff3a9371b1d78 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 28 Oct 2024 22:44:04 -0400 Subject: [PATCH 177/233] add ApiClientHelpers::broadcast_transaction method --- src/transport/client/helpers.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 47c96f9..8bd578f 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,6 +1,6 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, - GetAddressUtxosRequest, GetEventRequest}; + GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; @@ -16,6 +16,8 @@ pub enum HelperError { SelectUtxos(#[from] SelectUtxosError), #[error("ApiClientHelpers::get_event: failed to fetch event {0}")] GetEvent(ApiClientError), + #[error("ApiClientHelpers::broadcast_transaction: failed to broadcast transaction {0}")] + BroadcastTx(ApiClientError), } #[derive(Debug, Error)] @@ -243,4 +245,16 @@ pub trait ApiClientHelpers: ApiClient { wrong_variant => Err(GetTransactionError::EventVariant(wrong_variant))?, } } + + async fn broadcast_transaction(&self, tx: &V2Transaction) -> Result<(), HelperError> { + let request = TxpoolBroadcastRequest { + transactions: vec![], + v2transactions: vec![tx.clone()], + }; + + self.dispatcher(request) + .await + .map_err(|e| HelperError::BroadcastTx(e))?; + Ok(()) + } } From b2a7d3ffcd3586aadfe84fae132132fcfe7728bb Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 28 Oct 2024 22:45:36 -0400 Subject: [PATCH 178/233] fix Preimage serialization - fixes SatisfiedPolicy serde --- src/types/transaction.rs | 52 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 7af334c..e84d103 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -129,13 +129,59 @@ impl<'a> Encodable for CurrencyVersion<'a> { /// Preimage is a 32-byte array representing the preimage of a hash used in Sia's SpendPolicy::Hash /// Used to allow HLTC-style hashlock contracts in Sia -#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, From, Into)] -#[serde(transparent)] +#[derive(Clone, Debug, Default, PartialEq, From, Into)] pub struct Preimage(pub [u8; 32]); +impl Serialize for Preimage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Use hex::encode to convert the byte array to a lowercase hex string + let hex_string = hex::encode(&self.0); + serializer.serialize_str(&hex_string) + } +} + +impl<'de> Deserialize<'de> for Preimage { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PreimageVisitor; + + impl<'de> serde::de::Visitor<'de> for PreimageVisitor { + type Value = Preimage; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a 32 byte hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + // Ensure the length is correct for a 32 byte hex string (64 hex characters) + if value.len() != 64 { + return Err(E::invalid_length(value.len(), &self)); + } + + // Decode the hex string into a byte array + let mut bytes = [0u8; 32]; + hex::decode_to_slice(value, &mut bytes) + .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self))?; + + Ok(Preimage(bytes)) + } + } + + deserializer.deserialize_str(PreimageVisitor) + } +} + #[derive(Debug, Error)] pub enum PreimageError { - #[error("PreimageError: failed to convert from slice")] + #[error("PreimageError: failed to convert from slice invalid length: {0}")] InvalidSliceLength(usize), } From a136d4ffebc4071cabcde7d85268b70d07da9746 Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 29 Oct 2024 19:51:10 -0400 Subject: [PATCH 179/233] add V2TransactionBuilder::satisfy_atomic_swap_refund and V2TransactionBuilderError --- src/types/transaction.rs | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index e84d103..d813e2c 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1296,6 +1296,14 @@ impl Encodable for V2TransactionBuilder { } } +#[derive(Debug, Error)] +pub enum V2TransactionBuilderError { + #[error("V2TransactionBuilder::satisfy_atomic_swap_success: provided index: {index} is out of bounds for inputs of length: {len}")] + SatisfySuccessIndexOutOfBounds { len: usize, index: u32 }, + #[error("V2TransactionBuilder::satisfy_atomic_swap_refund: provided index: {index} is out of bounds for inputs of length: {len}")] + SatisfyRefundIndexOutOfBounds { len: usize, index: u32 }, +} + impl V2TransactionBuilder { pub fn new() -> Self { Self { @@ -1444,13 +1452,13 @@ impl V2TransactionBuilder { keypair: &Keypair, secret: Preimage, input_index: u32, - ) -> Result<&mut Self, SatisfyAtomicSwapSuccessError> { + ) -> Result<&mut Self, V2TransactionBuilderError> { let sig_hash = self.input_sig_hash(); let sig = keypair.sign(&sig_hash.0); // check input_index exists prior to indexing into the vector if self.siacoin_inputs.len() <= (input_index as usize) { - return Err(SatisfyAtomicSwapSuccessError::IndexOutOfBounds { + return Err(V2TransactionBuilderError::SatisfySuccessIndexOutOfBounds { len: self.siacoin_inputs.len(), index: input_index, }); @@ -1462,6 +1470,27 @@ impl V2TransactionBuilder { Ok(self) } + pub fn satisfy_atomic_swap_refund( + &mut self, + keypair: &Keypair, + input_index: u32, + ) -> Result<&mut Self, V2TransactionBuilderError> { + let sig_hash = self.input_sig_hash(); + let sig = keypair.sign(&sig_hash.0); + + // check input_index exists prior to indexing into the vector + if self.siacoin_inputs.len() <= (input_index as usize) { + return Err(V2TransactionBuilderError::SatisfyRefundIndexOutOfBounds { + len: self.siacoin_inputs.len(), + index: input_index, + }); + } + + let htlc_input = &mut self.siacoin_inputs[input_index as usize]; + htlc_input.satisfied_policy.signatures.push(sig); + Ok(self) + } + pub fn build(&mut self) -> V2Transaction { let cloned = self.clone(); V2Transaction { @@ -1483,9 +1512,3 @@ impl V2TransactionBuilder { impl Default for V2TransactionBuilder { fn default() -> Self { V2TransactionBuilder::new() } } - -#[derive(Debug, Error)] -pub enum SatisfyAtomicSwapSuccessError { - #[error("satisfy_atomic_swap_success: provided index: {index} is out of bounds for inputs of length: {len}")] - IndexOutOfBounds { len: usize, index: u32 }, -} From 80e2589738143307d5cf143b63f692a3ecf945be Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 30 Oct 2024 15:10:24 -0400 Subject: [PATCH 180/233] add ApiClientHelpers::get_address_events and corresponding errors --- src/transport/client/helpers.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 8bd578f..51595f6 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,6 +1,6 @@ use super::{ApiClient, ApiClientError}; -use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusTipRequest, - GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; +use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, AddressesEventsRequest, + ConsensusTipRequest, GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; @@ -16,6 +16,8 @@ pub enum HelperError { SelectUtxos(#[from] SelectUtxosError), #[error("ApiClientHelpers::get_event: failed to fetch event {0}")] GetEvent(ApiClientError), + #[error("ApiClientHelpers::get_address_events: failed {0}")] + GetAddressEvents(ApiClientError), #[error("ApiClientHelpers::broadcast_transaction: failed to broadcast transaction {0}")] BroadcastTx(ApiClientError), } @@ -235,6 +237,15 @@ pub trait ApiClientHelpers: ApiClient { .map_err(HelperError::GetEvent) } + async fn get_address_events(&self, address: Address) -> Result, HelperError> { + let request = AddressesEventsRequest { + address, + limit: None, + offset: None, + }; + self.dispatcher(request).await.map_err(HelperError::GetAddressEvents) + } + async fn get_transaction(&self, txid: &TransactionId) -> Result { let event = self .dispatcher(GetEventRequest { txid: txid.clone() }) From 25993f0ee633e917c85e25a25cba65b7b67e95d2 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 30 Oct 2024 22:53:14 -0400 Subject: [PATCH 181/233] add ApiClentHelpers::get_median_timestamp add partial impl of api/consensus/tipstate walletd endpoint --- src/transport/client/helpers.rs | 35 +++++++++++++++++++++++- src/transport/endpoints.rs | 48 +++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 51595f6..ab1c99a 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,6 +1,7 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, AddressesEventsRequest, - ConsensusTipRequest, GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; + ConsensusTipRequest, ConsensusTipstateRequest, ConsensusTipstateResponse, + GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; @@ -20,6 +21,8 @@ pub enum HelperError { GetAddressEvents(ApiClientError), #[error("ApiClientHelpers::broadcast_transaction: failed to broadcast transaction {0}")] BroadcastTx(ApiClientError), + #[error("ApiClientHelpers::get_median_timestamp: failed: {0}")] + GetMedianTimestamp(#[from] GetMedianTimestampError), } #[derive(Debug, Error)] @@ -56,6 +59,18 @@ pub enum SelectUtxosError { FetchUtxos(#[from] ApiClientError), } +#[derive(Debug, Error)] +pub enum GetMedianTimestampError { + #[error("ApiClientHelpers::get_median_timestamp: failed to fetch consensus tipstate: {0}")] + FetchTipstate(#[from] ApiClientError), + #[error( + r#"ApiClientHelpers::get_median_timestamp: expected 11 timestamps in response: {0:?}. + The walletd state is likely corrupt as it is evidently reporting a chain height of less + than 11 blocks."# + )] + TimestampVecLen(ConsensusTipstateResponse), +} + /// Helper methods for the ApiClient trait /// These generally provide higher level functionality than the base ApiClient trait /// This crate is focused on catering to the Komodo Defi Framework integration @@ -257,6 +272,24 @@ pub trait ApiClientHelpers: ApiClient { } } + /// Get the median timestamp of the chain's last 11 blocks + /// This is used in the evaluation of SpendPolicy::After + async fn get_median_timestamp(&self) -> Result { + let tipstate = self + .dispatcher(ConsensusTipstateRequest) + .await + .map_err(GetMedianTimestampError::FetchTipstate)?; + + // This can happen if the chain has less than 11 blocks + // We assume the chain is at least 11 blocks long for this helper. + if tipstate.prev_timestamps.len() != 11 { + return Err(GetMedianTimestampError::TimestampVecLen(tipstate))?; + } + + let median_timestamp = tipstate.prev_timestamps[5]; + Ok(median_timestamp.timestamp() as u64) + } + async fn broadcast_transaction(&self, tx: &V2Transaction) -> Result<(), HelperError> { let request = TxpoolBroadcastRequest { transactions: vec![], diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index d55104b..fb89c95 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,5 +1,6 @@ use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::types::{Address, BlockID, Currency, Event, Hash256, SiacoinElement, V1Transaction, V2Transaction}; +use crate::types::{Address, ChainIndex, Currency, Event, Hash256, SiacoinElement, V1Transaction, V2Transaction}; +use chrono::{DateTime, Utc}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -8,6 +9,7 @@ const ENDPOINT_ADDRESSES_BALANCE: &str = "api/addresses/{address}/balance"; const ENDPOINT_ADDRESSES_EVENTS: &str = "api/addresses/{address}/events"; const ENDPOINT_ADDRESSES_UTXOS_SIACOIN: &str = "api/addresses/{address}/outputs/siacoin"; const ENDPOINT_CONSENSUS_TIP: &str = "api/consensus/tip"; +const ENDPOINT_CONSENSUS_TIPSTATE: &str = "api/consensus/tipstate"; const ENDPOINT_EVENTS: &str = "api/events/{txid}"; const ENDPOINT_TXPOOL_BROADCAST: &str = "api/txpool/broadcast"; const ENDPOINT_TXPOOL_FEE: &str = "api/txpool/fee"; @@ -45,17 +47,53 @@ pub trait SiaApiRequest: Send { pub struct ConsensusTipRequest; impl SiaApiRequest for ConsensusTipRequest { - type Response = ConsensusTipResponse; + type Response = ChainIndex; fn to_endpoint_schema(&self) -> Result { Ok(EndpointSchemaBuilder::new(ENDPOINT_CONSENSUS_TIP.to_owned(), SchemaMethod::Get).build()) } } +// #[derive(Clone, Deserialize, Serialize, Debug)] +// pub struct ConsensusTipResponse { +// pub height: u64, +// pub id: BlockID, +// } + +/// Represents the request-response pair for fetching the current consensus tipstate of the Sia network. +/// +/// # Walletd Endpoint +/// `GET /consensus/tipstate` +/// +/// # Description +/// Returns the current consensus state of the Sia network. +/// +/// # Response +/// - The response is a `ConsensusTipstateResponse`, which is a partial implementation of the `consensus.State` type in Go. +/// This response includes the current block's height and ID, as well as timestamps of the previous 11 blocks. +/// The median of the provided timestamps is the medianTimestamp used to evaluate SpendPolicy::After. +/// SpendPolicy::After(time) evaluates to true if `time > medianTimestamp`. +/// +/// # References +/// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/d71cf08d4579ba952c51e535f988000e43ed8722/api/server.go#L162) +/// - [Go Source for the consensus.State Type](https://github.com/SiaFoundation/core/blob/00682daf422864b250b6bc750d4229dd76a8632d/consensus/state.go#L111) +/// +/// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct ConsensusTipstateRequest; + +impl SiaApiRequest for ConsensusTipstateRequest { + type Response = ConsensusTipstateResponse; + + fn to_endpoint_schema(&self) -> Result { + Ok(EndpointSchemaBuilder::new(ENDPOINT_CONSENSUS_TIPSTATE.to_owned(), SchemaMethod::Get).build()) + } +} + #[derive(Clone, Deserialize, Serialize, Debug)] -pub struct ConsensusTipResponse { - pub height: u64, - pub id: BlockID, +pub struct ConsensusTipstateResponse { + pub index: ChainIndex, + pub prev_timestamps: Vec>, } /// Represents the request-response pair for fetching the balance of an individual address. From 0937be8cb899532702db672de1a9d858da59451e Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 30 Oct 2024 22:53:56 -0400 Subject: [PATCH 182/233] remove commented dead code --- src/transport/endpoints.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index fb89c95..b09f70b 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -54,12 +54,6 @@ impl SiaApiRequest for ConsensusTipRequest { } } -// #[derive(Clone, Deserialize, Serialize, Debug)] -// pub struct ConsensusTipResponse { -// pub height: u64, -// pub id: BlockID, -// } - /// Represents the request-response pair for fetching the current consensus tipstate of the Sia network. /// /// # Walletd Endpoint From 548f67b570645162f8ac8abef2165c251a86842c Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 06:02:49 -0400 Subject: [PATCH 183/233] add api/consensus/index/{height} and WIP api/consensus/updates/{height}::{hash} walletd endpoints --- src/transport/client/helpers.rs | 67 ++++++++++++++- src/transport/endpoints.rs | 144 +++++++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 6 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index ab1c99a..fd13c3e 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -1,9 +1,10 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, AddressesEventsRequest, - ConsensusTipRequest, ConsensusTipstateRequest, ConsensusTipstateResponse, + ConsensusIndexRequest, ConsensusTipRequest, ConsensusTipstateRequest, + ConsensusTipstateResponse, ConsensusUpdatesRequest, ConsensusUpdatesResponse, GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; -use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, - SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; +use crate::types::{Address, ChainIndex, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, + SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; use thiserror::Error; @@ -23,6 +24,8 @@ pub enum HelperError { BroadcastTx(ApiClientError), #[error("ApiClientHelpers::get_median_timestamp: failed: {0}")] GetMedianTimestamp(#[from] GetMedianTimestampError), + #[error("ApiClientHelpers::get_consensus_updates_since_height: failed {0}")] + UpdatesSinceHeight(#[from] UpdatesSinceHeightError), } #[derive(Debug, Error)] @@ -301,4 +304,62 @@ pub trait ApiClientHelpers: ApiClient { .map_err(|e| HelperError::BroadcastTx(e))?; Ok(()) } + + async fn get_consensus_updates_since_height( + &self, + begin_height: u64, + ) -> Result { + let index_request = ConsensusIndexRequest { height: begin_height }; + let chain_index = self + .dispatcher(index_request) + .await + .map_err(UpdatesSinceHeightError::FetchIndex)?; + + let updates_request = ConsensusUpdatesRequest { + height: chain_index.height, + block_hash: chain_index.id, + limit: None, + }; + + self.dispatcher(updates_request) + .await + .map_err(UpdatesSinceHeightError::FetchUpdates) + .map_err(HelperError::UpdatesSinceHeight) + } + + /// Find the transaction that spent the given utxo + /// Scans the blockchain starting from `begin_height` + /// Returns Ok(None) if the utxo has not been spent + async fn find_where_utxo_spent( + &self, + siacoin_output_id: &SiacoinOutputId, + begin_height: u64, + ) -> Result, HelperError> { + // the SiacoinOutputId is displayed with h: prefix in the endpoint response so use we Hash256 + let output_id = siacoin_output_id.0.clone(); + + // let updates = self.get_consensus_updates_since_height(begin_height).await?; + // // // find the update that has siacoin_output_id in "spent" field + // updates.applied.iter().find_map(|applied_update| { + // if applied_update.update.spent.contains(&output_id) { + // // if the output is in the spent field, we must search the corresponding block for the + // // transaction that spent the output + // applied_update.block.v2.transactions.iter().find_map(|tx| { + // // find the one that spend the output + // todo!() + // }) + // } else { + // None + // } + // }); + todo!() + } +} + +#[derive(Debug, Error)] +pub enum UpdatesSinceHeightError { + #[error("ApiClientHelpers::get_consensus_updates_since_height: failed to fetch ChainIndex {0}")] + FetchIndex(ApiClientError), + #[error("ApiClientHelpers::get_consensus_updates_since_height: failed to fetch updates {0}")] + FetchUpdates(ApiClientError), } diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index b09f70b..2b8c35f 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,15 +1,18 @@ use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::types::{Address, ChainIndex, Currency, Event, Hash256, SiacoinElement, V1Transaction, V2Transaction}; +use crate::types::{Address, BlockID, ChainIndex, Currency, Event, Hash256, SiacoinElement, V1Transaction, + V2Transaction}; use chrono::{DateTime, Utc}; use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::collections::HashMap; const ENDPOINT_ADDRESSES_BALANCE: &str = "api/addresses/{address}/balance"; const ENDPOINT_ADDRESSES_EVENTS: &str = "api/addresses/{address}/events"; const ENDPOINT_ADDRESSES_UTXOS_SIACOIN: &str = "api/addresses/{address}/outputs/siacoin"; const ENDPOINT_CONSENSUS_TIP: &str = "api/consensus/tip"; +const ENDPOINT_CONSENSUS_INDEX: &str = "api/consensus/index/{height}"; const ENDPOINT_CONSENSUS_TIPSTATE: &str = "api/consensus/tipstate"; +const ENDPOINT_CONSENSUS_UPDATES: &str = "api/consensus/updates/{height}::{hash}"; const ENDPOINT_EVENTS: &str = "api/events/{txid}"; const ENDPOINT_TXPOOL_BROADCAST: &str = "api/txpool/broadcast"; const ENDPOINT_TXPOOL_FEE: &str = "api/txpool/fee"; @@ -35,7 +38,7 @@ pub trait SiaApiRequest: Send { /// and its block ID, representing the latest state of the blockchain. /// /// # Response -/// - The response is a `ConsensusTipResponse`, which contains the block's height and ID. +/// - The response is a `ChainIndex`, which contains the block's height and ID. /// This corresponds to the `types.ChainIndex` type in Go. /// /// # References @@ -54,6 +57,45 @@ impl SiaApiRequest for ConsensusTipRequest { } } +/// Represents the request-response pair for fetching a BlockId from a provided height. +/// +/// # Walletd Endpoint +/// `GET /consensus/index/{height}` +/// +/// # Description +/// Returns the ChainIndex of the Block at the provided height. The consensus tip includes the block's height +/// and its BlockId. +/// +/// # Response +/// - The response is a `ChainIndex`, which contains the block's height and ID. +/// This corresponds to the `types.ChainIndex` type in Go. +/// +/// # References +/// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/6ff23fe34f6fa45a19bfb6e4bacc8a16d2c48144/api/server.go#L158) +/// - [Go Source for the ChainIndex Type](https://github.com/SiaFoundation/core/blob/300042fd2129381468356dcd87c5e9a6ad94c0ef/types/types.go#L194) +/// +/// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct ConsensusIndexRequest { + pub height: u64, +} + +impl SiaApiRequest for ConsensusIndexRequest { + type Response = ChainIndex; + + fn to_endpoint_schema(&self) -> Result { + // Create the path_params HashMap to substitute {height} in the path schema + let mut path_params = HashMap::new(); + path_params.insert("height".to_owned(), self.height.to_string()); + + Ok( + EndpointSchemaBuilder::new(ENDPOINT_CONSENSUS_INDEX.to_owned(), SchemaMethod::Get) + .path_params(path_params) + .build(), + ) + } +} + /// Represents the request-response pair for fetching the current consensus tipstate of the Sia network. /// /// # Walletd Endpoint @@ -90,6 +132,102 @@ pub struct ConsensusTipstateResponse { pub prev_timestamps: Vec>, } +/// Represents the request-response pair for fetching consensus updates of the Sia network. +/// +/// # Walletd Endpoint +/// `GET /consensus/updates/{height}::{hash}` +/// +/// # Description +/// Returns consensus updates since the specific block height until the current consensus tip. +/// +/// # Response +/// - The response is a `ConsensusUpdatesResponse`, which is a partial implementation of the `ConsensusUpdatesResponse` type in Go. +/// This endpoint only partially implements the Go type provided as response. +/// This endpoints implements the minimum required fields to facilitate the logic of ApiClientHelpers::find_where_utxo_spent method. +/// +/// # References +/// - [Go Source for the HTTP Endpoint](https://github.com/SiaFoundation/walletd/blob/d71cf08d4579ba952c51e535f988000e43ed8722/api/server.go#L162) +/// - [Go Source for the consensus.State Type](https://github.com/SiaFoundation/core/blob/00682daf422864b250b6bc750d4229dd76a8632d/consensus/state.go#L111) +/// +/// This type is ported from the Go codebase, representing the equivalent request-response pair in Rust. +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct ConsensusUpdatesRequest { + pub height: u64, + pub block_hash: BlockID, + pub limit: Option, +} + +impl SiaApiRequest for ConsensusUpdatesRequest { + type Response = ConsensusUpdatesResponse; + + fn to_endpoint_schema(&self) -> Result { + // Create the path_params HashMap to substitute {height} and {hash} in the path schema + let mut path_params = HashMap::new(); + path_params.insert("height".to_owned(), self.height.to_string()); + path_params.insert("hash".to_owned(), format!("{:02x}", self.block_hash.0)); + + let mut query_params = HashMap::new(); + if let Some(limit) = self.limit { + query_params.insert("limit".to_owned(), limit.to_string()); + } + + Ok( + EndpointSchemaBuilder::new(ENDPOINT_CONSENSUS_UPDATES.to_owned(), SchemaMethod::Get) + .path_params(path_params) // Set the path params containing the height and hash + .query_params(query_params) // Set the query params for ?limit= + .build(), + ) + } +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ConsensusUpdatesResponse { + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub applied: Vec, +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ApplyUpdate { + pub update: Update, + pub block: Block, + // pub state: State, // This field is not implemented in the Rust +} + +/// Equivalent of Go type `consensus.ApplyUpdate` +/// Many fields are not implemented here in rust because we only need the SiacoinOutputId's found +/// in the `spent` field for ApiClientHelpers::find_where_utxo_spent method. +#[derive(Clone, Serialize, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Update { + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub spent: Vec, +} + +/// Deserialize a null value as an empty vector. +fn deserialize_null_as_empty_vec<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::deserialize(deserializer).map(|opt| opt.unwrap_or_default()) +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Block { + pub v2: V2BlockData, +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct V2BlockData { + pub height: u64, + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub transactions: Vec, +} + /// Represents the request-response pair for fetching the balance of an individual address. /// /// # Walletd Endpoint From 7e8beb936e4ccf520294fbe66b559a91c80ba09e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:37:16 -0400 Subject: [PATCH 184/233] rename macros module to utils add deserialize_null_as_empty_vec to utils module --- src/lib.rs | 3 ++- src/utils.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/utils.rs diff --git a/src/lib.rs b/src/lib.rs index 9878c83..8f912ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #[macro_use] -mod macros; +pub(crate) mod utils; +// TODO Alright - if this is truly "internal" it should not be public pub mod blake2b_internal; pub mod encoding; pub mod transport; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..2e91f96 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,34 @@ +use serde::{Deserialize, Deserializer}; + +/// Run a block of unit tests on both wasm32 and non-wasm32 targets +/// Can only be used once per scope due to wasm_bindgen initialization +#[cfg(test)] +macro_rules! cross_target_tests { + ($($test_fn:item)*) => { + #[cfg(all(test, target_arch = "wasm32"))] + use wasm_bindgen_test::*; + + #[cfg(all(test, target_arch = "wasm32"))] + wasm_bindgen_test_configure!(run_in_browser); + + $( + #[cfg(all(test, target_arch = "wasm32"))] + #[wasm_bindgen_test] + $test_fn + + #[cfg(not(target_arch = "wasm32"))] + #[test] + $test_fn + )* + }; +} + +/// Deserialize a null value as an empty vector. +/// Allows using Vec<> instead of Option> for convenience. +pub(crate) fn deserialize_null_as_empty_vec<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::deserialize(deserializer).map(|opt| opt.unwrap_or_default()) +} From 7a19d422a8181f43ca59f0e4fdd3d530e5de904c Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:37:27 -0400 Subject: [PATCH 185/233] delete old macros module --- src/macros.rs | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/macros.rs diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 0298d92..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,22 +0,0 @@ -/// Run a block of unit tests on both wasm32 and non-wasm32 targets -/// Can only be used once per scope due to wasm_bindgen initialization -#[cfg(test)] -macro_rules! cross_target_tests { - ($($test_fn:item)*) => { - #[cfg(all(test, target_arch = "wasm32"))] - use wasm_bindgen_test::*; - - #[cfg(all(test, target_arch = "wasm32"))] - wasm_bindgen_test_configure!(run_in_browser); - - $( - #[cfg(all(test, target_arch = "wasm32"))] - #[wasm_bindgen_test] - $test_fn - - #[cfg(not(target_arch = "wasm32"))] - #[test] - $test_fn - )* - }; -} From bd2ba0d5fb8eb6aea1c4fa385a5dcc2698f83915 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:37:39 -0400 Subject: [PATCH 186/233] add consensus_updates module --- src/types.rs | 3 +++ src/types/consensus_updates.rs | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/types/consensus_updates.rs diff --git a/src/types.rs b/src/types.rs index 70f1b09..58d0144 100644 --- a/src/types.rs +++ b/src/types.rs @@ -28,6 +28,9 @@ pub use transaction::*; mod specifier; pub use specifier::*; +mod consensus_updates; +pub use consensus_updates::*; + const ADDRESS_HASH_LENGTH: usize = 32; const ADDRESS_CHECKSUM_LENGTH: usize = 6; diff --git a/src/types/consensus_updates.rs b/src/types/consensus_updates.rs new file mode 100644 index 0000000..1ab5395 --- /dev/null +++ b/src/types/consensus_updates.rs @@ -0,0 +1,46 @@ +use crate::types::{Hash256, V2Transaction}; +use crate::utils::deserialize_null_as_empty_vec; + +use serde::{Deserialize, Serialize}; + +/// This module consists of types related to walletd's `api/consensus/updates/:index` endpoint. +/// Only a partial implementation is done here to facilitate `ApiClientHelpers::find_where_utxo_spent` +/// It's possible these may be extended in the future, so a dedicated +/// module is created for this. + +/// Minimal implementation of Go type `api.ApplyUpdate` +/// As per walletd: "An ApplyUpdate is a consensus update that was applied to the best chain." +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ApiApplyUpdate { + pub update: Update, + pub block: Block, +} + +/// Minimal implementation of Go type `consensus.ApplyUpdate` +/// As per sia-core: "An ApplyUpdate represents the effects of applying a block to a state." +#[derive(Clone, Serialize, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Update { + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub spent: Vec, +} + +/// Minimal implementation of Go type `types.Block` +/// As per sia-core: "A Block is a set of transactions grouped under a header." +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Block { + pub v2: V2BlockData, +} + +/// Equivalent of Go type `types.V2BlockData` +/// As per sia-core: "V2BlockData contains additional fields not present in v1 blocks."" +#[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct V2BlockData { + pub height: u64, + pub commitment: Hash256, + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub transactions: Vec, +} From 64c8ce9fcc4ef602493963ab8394295ad7f0fcd9 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:38:17 -0400 Subject: [PATCH 187/233] remove consensus updates types from endpoints module - moved to dedicated module --- src/transport/endpoints.rs | 50 ++++---------------------------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 2b8c35f..ddc48dd 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,9 +1,10 @@ use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::types::{Address, BlockID, ChainIndex, Currency, Event, Hash256, SiacoinElement, V1Transaction, - V2Transaction}; +use crate::types::{Address, ApiApplyUpdate, BlockID, ChainIndex, Currency, Event, Hash256, SiacoinElement, + V1Transaction, V2Transaction}; +use crate::utils::deserialize_null_as_empty_vec; use chrono::{DateTime, Utc}; use serde::de::DeserializeOwned; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; const ENDPOINT_ADDRESSES_BALANCE: &str = "api/addresses/{address}/balance"; @@ -184,48 +185,7 @@ impl SiaApiRequest for ConsensusUpdatesRequest { #[serde(rename_all = "camelCase")] pub struct ConsensusUpdatesResponse { #[serde(deserialize_with = "deserialize_null_as_empty_vec")] - pub applied: Vec, -} - -#[derive(Clone, Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ApplyUpdate { - pub update: Update, - pub block: Block, - // pub state: State, // This field is not implemented in the Rust -} - -/// Equivalent of Go type `consensus.ApplyUpdate` -/// Many fields are not implemented here in rust because we only need the SiacoinOutputId's found -/// in the `spent` field for ApiClientHelpers::find_where_utxo_spent method. -#[derive(Clone, Serialize, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Update { - #[serde(deserialize_with = "deserialize_null_as_empty_vec")] - pub spent: Vec, -} - -/// Deserialize a null value as an empty vector. -fn deserialize_null_as_empty_vec<'de, D, T>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, - T: Deserialize<'de>, -{ - Option::deserialize(deserializer).map(|opt| opt.unwrap_or_default()) -} - -#[derive(Clone, Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct Block { - pub v2: V2BlockData, -} - -#[derive(Clone, Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct V2BlockData { - pub height: u64, - #[serde(deserialize_with = "deserialize_null_as_empty_vec")] - pub transactions: Vec, + pub applied: Vec, } /// Represents the request-response pair for fetching the balance of an individual address. From d58aa1ea221cf5130681f885b10558563dabad2a Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:39:08 -0400 Subject: [PATCH 188/233] Edit HelperError display impls add HelperError::FindWhereUtxoSpent --- src/transport/client/helpers.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index fd13c3e..616dcd8 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -10,22 +10,24 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum HelperError { - #[error("ApiClientHelpers::utxo_from_txid: {0}")] + #[error("ApiClientHelpers::utxo_from_txid failed: {0}")] UtxoFromTxid(#[from] UtxoFromTxidError), - #[error("ApiClientHelpers::get_transaction: {0}")] + #[error("ApiClientHelpers::get_transaction failed: {0}")] GetTx(#[from] GetTransactionError), - #[error("ApiClientHelpers::select_unspent_outputs: {0}")] + #[error("ApiClientHelpers::select_unspent_outputs failed: {0}")] SelectUtxos(#[from] SelectUtxosError), - #[error("ApiClientHelpers::get_event: failed to fetch event {0}")] + #[error("ApiClientHelpers::get_event failed to fetch event: {0}")] GetEvent(ApiClientError), - #[error("ApiClientHelpers::get_address_events: failed {0}")] + #[error("ApiClientHelpers::get_address_events failed: {0}")] GetAddressEvents(ApiClientError), - #[error("ApiClientHelpers::broadcast_transaction: failed to broadcast transaction {0}")] + #[error("ApiClientHelpers::broadcast_transaction failed to broadcast transaction: {0}")] BroadcastTx(ApiClientError), - #[error("ApiClientHelpers::get_median_timestamp: failed: {0}")] + #[error("ApiClientHelpers::get_median_timestamp failed: {0}")] GetMedianTimestamp(#[from] GetMedianTimestampError), - #[error("ApiClientHelpers::get_consensus_updates_since_height: failed {0}")] + #[error("ApiClientHelpers::get_consensus_updates_since_height failed: {0}")] UpdatesSinceHeight(#[from] UpdatesSinceHeightError), + #[error("ApiClientHelpers::find_where_utxo_spent failed: {0}")] + FindWhereUtxoSpent(#[from] FindWhereUtxoSpentError), } #[derive(Debug, Error)] From 2e67cb59b045516a4d99c78491f348c11383c18c Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 31 Oct 2024 10:39:43 -0400 Subject: [PATCH 189/233] finish implementing ApiClientHelpers::find_where_utxo_spent --- src/transport/client/helpers.rs | 60 +++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 616dcd8..e3ace3e 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -3,8 +3,8 @@ use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, ConsensusIndexRequest, ConsensusTipRequest, ConsensusTipstateRequest, ConsensusTipstateResponse, ConsensusUpdatesRequest, ConsensusUpdatesResponse, GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; -use crate::types::{Address, ChainIndex, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, - SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; +use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, + SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; use thiserror::Error; @@ -340,24 +340,50 @@ pub trait ApiClientHelpers: ApiClient { // the SiacoinOutputId is displayed with h: prefix in the endpoint response so use we Hash256 let output_id = siacoin_output_id.0.clone(); - // let updates = self.get_consensus_updates_since_height(begin_height).await?; - // // // find the update that has siacoin_output_id in "spent" field - // updates.applied.iter().find_map(|applied_update| { - // if applied_update.update.spent.contains(&output_id) { - // // if the output is in the spent field, we must search the corresponding block for the - // // transaction that spent the output - // applied_update.block.v2.transactions.iter().find_map(|tx| { - // // find the one that spend the output - // todo!() - // }) - // } else { - // None - // } - // }); - todo!() + let updates = self + .get_consensus_updates_since_height(begin_height) + .await + .map_err(|e| FindWhereUtxoSpentError::FetchUpdates(Box::new(e)))?; + + // find the update that has the provided `siacoin_output_id`` in its "spent" field + let update_option = updates + .applied + .into_iter() + .find(|applied_update| applied_update.update.spent.contains(&output_id)); + + // If no update with the output_id was found, return Ok(None) indicating no error occured, + // but the spend transaction has not been found + let update = match update_option { + Some(update) => update, + None => return Ok(None), + }; + + // scan the block indicated by the update to find the transaction that spent the utxo + let tx = update + .block + .v2 + .transactions + .into_iter() + .find(|tx| { + tx.siacoin_inputs + .iter() + .any(|input| input.parent.state_element.id == output_id) + }) + .ok_or(FindWhereUtxoSpentError::SpendNotInBlock { id: output_id })?; + + Ok(Some(tx)) } } +#[derive(Debug, Error)] +pub enum FindWhereUtxoSpentError { + // Boxed to allow HelperError to be held within itself + #[error("ApiClientHelpers::find_where_utxo_spent: failed to fetch consensus updates {0}")] + FetchUpdates(#[from] Box), + #[error("ApiClientHelpers::find_where_utxo_spent: scoid:{id} was not spent in the expected block")] + SpendNotInBlock { id: Hash256 }, +} + #[derive(Debug, Error)] pub enum UpdatesSinceHeightError { #[error("ApiClientHelpers::get_consensus_updates_since_height: failed to fetch ChainIndex {0}")] From 510f36d72e84b95f1f8fae48bb2d70678c12ed27 Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 5 Nov 2024 10:03:58 -0500 Subject: [PATCH 190/233] add newly added "confirmations" field to Event type add a FIXME dev comment regarding Deserialize impl of Event --- src/types.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index 58d0144..01d1212 100644 --- a/src/types.rs +++ b/src/types.rs @@ -263,6 +263,7 @@ pub enum EventType { pub struct Event { pub id: Hash256, pub index: ChainIndex, + pub confirmations: u64, pub timestamp: DateTime, #[serde(rename = "maturityHeight")] pub maturity_height: u64, @@ -282,6 +283,7 @@ impl<'de> Deserialize<'de> for Event { struct EventHelper { id: Hash256, index: ChainIndex, + confirmations: u64, timestamp: DateTime, #[serde(rename = "maturityHeight")] maturity_height: u64, @@ -309,7 +311,8 @@ impl<'de> Deserialize<'de> for Event { .map(EventDataWrapper::V2Transaction) .map_err(serde::de::Error::custom), EventType::V1ContractResolution => { - return Err(serde::de::Error::custom("V1ContractResolution not supported")) + // FIXME we require this to safely deser V2Transactions sent over the wire + return Err(serde::de::Error::custom("V1ContractResolution not supported")); }, EventType::V2ContractResolution => serde_json::from_value::(helper.data) .map(|data| EventDataWrapper::V2FileContractResolution(Box::new(data))) @@ -319,6 +322,7 @@ impl<'de> Deserialize<'de> for Event { Ok(Event { id: helper.id, index: helper.index, + confirmations: helper.confirmations, timestamp: helper.timestamp, maturity_height: helper.maturity_height, event_type: helper.event_type, From e8dbc0c3313ecf1ccf6d00d4d7b495911bdf5298 Mon Sep 17 00:00:00 2001 From: Alright Date: Wed, 6 Nov 2024 14:55:20 -0500 Subject: [PATCH 191/233] add dev comment TODO re: EmptyResponse type --- src/transport/endpoints.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index ddc48dd..2d6d5d0 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -421,6 +421,8 @@ pub struct TxpoolBroadcastRequest { pub v2transactions: Vec, } +// TODO Alright - this was initially thought neccesary to implement methods on it, but it seems () +// will work in its place #[derive(Clone, Deserialize, Serialize, Debug)] pub struct EmptyResponse; From 21da3c164fcb13915ca177e485f5545719ac8188 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 7 Nov 2024 09:24:29 -0500 Subject: [PATCH 192/233] fix txpool endpoint response --- src/transport/endpoints.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 2d6d5d0..343b6ef 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -501,10 +501,17 @@ impl SiaApiRequest for TxpoolFeeRequest { #[derive(Clone, Deserialize, Serialize, Debug)] pub struct TxpoolTransactionsRequest; -impl SiaApiRequest for TxpoolTransactionsRequest { - type Response = EmptyResponse; +#[derive(Clone, Deserialize, Serialize, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct TxpoolTransactionsResponse { + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub transactions: Vec, + #[serde(deserialize_with = "deserialize_null_as_empty_vec")] + pub v2transactions: Vec, +} - fn is_empty_response() -> Option { Some(EmptyResponse) } +impl SiaApiRequest for TxpoolTransactionsRequest { + type Response = TxpoolTransactionsResponse; fn to_endpoint_schema(&self) -> Result { Ok( From d162f2e588821e03b5936489915ca5b8b34f5f4a Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 20:08:04 -0500 Subject: [PATCH 193/233] fix various cargo doc hyperlinks --- src/blake2b_internal.rs | 2 +- src/types/keypair.rs | 2 +- src/types/transaction.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index 39fb04a..a8df535 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -24,7 +24,7 @@ const STANDARD_SIGS_REQUIRED_BLAKE2B_HASH: [u8; 32] = [ // FIXME remove direct indexing of arrays or add sanity checks to prevent out of bound access /// Directly ported from Sia core -/// https://github.com/SiaFoundation/core/blob/0f61e58ab7ea932da7e9f710c592d595406356c6/internal/blake2b/blake2b.go#L66 +/// #[derive(Debug, PartialEq)] pub struct Accumulator { trees: [Hash256; 64], diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 812eb54..5d2c7bc 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -29,7 +29,7 @@ pub enum PrivateKeyError { /// The purpose of this wrapper type is to limit the functionality of underlying ed25519 types. /// The inner fields are not public by design. /// We must not allow the consumer to create an invalid ed25519 Keypair or edit the PublicKey after creation. -/// see https://github.com/advisories/GHSA-w5vr-6qhr-36cc +/// see pub struct Keypair { public: PublicKey, private: PrivateKey, diff --git a/src/types/transaction.rs b/src/types/transaction.rs index d813e2c..3bfdbc0 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -316,7 +316,7 @@ impl Encodable for SiafundElement { /// This type is effectively a "UTXO" in Bitcoin terms. /// A SiacoinElement can be combined with a SatisfiedPolicy to create a SiacoinInputV2. /// Ported from Sia Core: -/// https://github.com/SiaFoundation/core/blob/b7ccbe54cccba5642c2bb9d721967214a4ba4e97/types/types.go#L619 +/// #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiacoinElement { @@ -434,7 +434,7 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { /// This could be a newtype like SiacoinOutputId with custom serde, but we have no use for this beyond /// making SiacoinOutputId::new more explicit. /// Sia Go uses "txid:" prefix for this type, but walletd API generally represents this with the -/// more generic "h:" prefix. related: https://github.com/SiaFoundation/core/pull/199 +/// more generic "h:" prefix. related: pub type TransactionId = Hash256; #[derive(Clone, Debug, PartialEq, From, Into)] From 317aabf76deceee8eb02126be6901047027e1961 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 20:16:29 -0500 Subject: [PATCH 194/233] fix expected case for test_satisfied_policy_encode_hash_empty unit test --- src/tests/transaction.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 070f191..e009033 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -188,8 +188,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:51eb259ed69313a81d72ea5ee1eb7c7111469e66720f2faef0a19054f959d375").unwrap(); + let expected = Hash256::from_str("h:1e612d1ee36338b93a36bac0c52007a2d678cde0bd9b95c36a1f61166cf02b87").unwrap(); assert_eq!(hash, expected); } From 49d250bed026415386a91900b39a4caaed934eed Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 20:18:30 -0500 Subject: [PATCH 195/233] fix SpendPolicy::Hash encoding to account for fixed sized preimages change Prior to this change, preimages included in SatisfiedPolicy were Vec They are now [u8; 32] --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 3bfdbc0..73e106f 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -238,7 +238,7 @@ impl Encodable for SatisfiedPolicy { }, SpendPolicy::Hash(_) => { if *prei < sp.preimages.len() { - encoder.write_len_prefixed_bytes(&sp.preimages[*prei].0); + encoder.write_slice(&sp.preimages[*prei].0); *prei += 1; } else { // Sia Go code panics here but our code assumes encoding will always be successful From 51e5df2b861dc8ac8cd6b1b6be16b270005e6fd1 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 22:07:58 -0500 Subject: [PATCH 196/233] remove id field from StateElement introduce id field to SiafundElement and SiacoinElement remove scoid: prefix for serde of SiacoinOutputId change merkle_proof field of StateElement from Option> to Vec - serde(default) should handle both `null` and `[]` as empty Vec<> Leave FIXME doc comment regarding the need for additional unit tests for these changes --- src/transport/client/helpers.rs | 16 ++--- src/types/transaction.rs | 105 +++++++++++--------------------- 2 files changed, 41 insertions(+), 80 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index e3ace3e..2c56bf9 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -234,7 +234,7 @@ pub trait ApiClientHelpers: ApiClient { // filter the utxos to find any matching the expected SiacoinOutputId let filtered_utxos: Vec = address_utxos .into_iter() - .filter(|element| element.state_element.id == output_id.clone().into()) + .filter(|element| element.id == output_id) .collect(); // ensure only one utxo was found @@ -338,7 +338,7 @@ pub trait ApiClientHelpers: ApiClient { begin_height: u64, ) -> Result, HelperError> { // the SiacoinOutputId is displayed with h: prefix in the endpoint response so use we Hash256 - let output_id = siacoin_output_id.0.clone(); + let output_id = siacoin_output_id; let updates = self .get_consensus_updates_since_height(begin_height) @@ -349,7 +349,7 @@ pub trait ApiClientHelpers: ApiClient { let update_option = updates .applied .into_iter() - .find(|applied_update| applied_update.update.spent.contains(&output_id)); + .find(|applied_update| applied_update.update.spent.contains(&output_id.0)); // If no update with the output_id was found, return Ok(None) indicating no error occured, // but the spend transaction has not been found @@ -364,12 +364,8 @@ pub trait ApiClientHelpers: ApiClient { .v2 .transactions .into_iter() - .find(|tx| { - tx.siacoin_inputs - .iter() - .any(|input| input.parent.state_element.id == output_id) - }) - .ok_or(FindWhereUtxoSpentError::SpendNotInBlock { id: output_id })?; + .find(|tx| tx.siacoin_inputs.iter().any(|input| input.parent.id == *output_id)) + .ok_or(FindWhereUtxoSpentError::SpendNotInBlock { id: output_id.clone() })?; Ok(Some(tx)) } @@ -381,7 +377,7 @@ pub enum FindWhereUtxoSpentError { #[error("ApiClientHelpers::find_where_utxo_spent: failed to fetch consensus updates {0}")] FetchUpdates(#[from] Box), #[error("ApiClientHelpers::find_where_utxo_spent: scoid:{id} was not spent in the expected block")] - SpendNotInBlock { id: Hash256 }, + SpendNotInBlock { id: SiacoinOutputId }, } #[derive(Debug, Error)] diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 73e106f..0f0c3d9 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -270,27 +270,22 @@ impl Encodable for SatisfiedPolicy { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct StateElement { - pub id: Hash256, pub leaf_index: u64, #[serde(default)] - pub merkle_proof: Option>, + pub merkle_proof: Vec, } +// FIXME Alright requires new unit tests and corresponding rust_port_test.go tests +// merkle_proof was previously Option> because Walletd can return null for this field +// Test unintialized slice (ie, null) vs empty slice - do they encode the same? +// the following encoding assumes that they do encode the same impl Encodable for StateElement { fn encode(&self, encoder: &mut Encoder) { - self.id.encode(encoder); encoder.write_u64(self.leaf_index); + encoder.write_u64(self.merkle_proof.len() as u64); - match &self.merkle_proof { - Some(proof) => { - encoder.write_u64(proof.len() as u64); - for p in proof { - p.encode(encoder); - } - }, - None => { - encoder.write_u64(0u64); - }, + for proof in &self.merkle_proof { + proof.encode(encoder); } } } @@ -298,7 +293,8 @@ impl Encodable for StateElement { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiafundElement { - #[serde(flatten)] + #[serde(rename = "ID")] + pub id: SiafundOutputId, pub state_element: StateElement, pub siafund_output: SiafundOutput, pub claim_start: Currency, @@ -320,7 +316,8 @@ impl Encodable for SiafundElement { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiacoinElement { - #[serde(flatten)] + #[serde(rename = "ID")] + pub id: SiacoinOutputId, pub state_element: StateElement, pub siacoin_output: SiacoinOutput, pub maturity_height: u64, @@ -437,9 +434,14 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { /// more generic "h:" prefix. related: pub type TransactionId = Hash256; -#[derive(Clone, Debug, PartialEq, From, Into)] +#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display)] +#[serde(transparent)] pub struct SiacoinOutputId(pub Hash256); +impl Encodable for SiacoinOutputId { + fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder) } +} + impl SiacoinOutputId { pub fn new(txid: TransactionId, index: u32) -> Self { let mut encoder = Encoder::default(); @@ -450,49 +452,12 @@ impl SiacoinOutputId { } } -impl<'de> Deserialize<'de> for SiacoinOutputId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct SiacoinOutputIdVisitor; - - impl<'de> serde::de::Visitor<'de> for SiacoinOutputIdVisitor { - type Value = SiacoinOutputId; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'scoid:' and followed by a 64-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("scoid:") { - Hash256::from_str_no_prefix(hex_str) - .map(SiacoinOutputId) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(SiacoinOutputIdVisitor) - } -} - -impl Serialize for SiacoinOutputId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{}", self)) - } -} +#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display)] +#[serde(transparent)] +pub struct SiafundOutputId(pub Hash256); -impl fmt::Display for SiacoinOutputId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "scoid:{:02x}", self.0) } +impl Encodable for SiafundOutputId { + fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder) } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -661,7 +626,8 @@ impl Encodable for V2FileContract { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct V2FileContractElement { - #[serde(flatten)] + #[serde(rename = "ID")] + pub id: FileContractID, pub state_element: StateElement, pub v2_file_contract: V2FileContract, } @@ -749,7 +715,6 @@ impl Encodable for StorageProof { } } -type SiafundOutputID = Hash256; type FileContractID = Hash256; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -783,7 +748,7 @@ impl Encodable for FileContractRevision { #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SiafundInputV1 { - pub parent_id: SiafundOutputID, + pub parent_id: SiafundOutputId, pub unlock_condition: UnlockCondition, pub claim_address: Address, } @@ -960,7 +925,7 @@ impl V2StorageProof { V2StorageProof { proof_index: ChainIndexElement { state_element: StateElement { - merkle_proof: None, + merkle_proof: vec![], ..self.proof_index.state_element.clone() }, ..self.proof_index.clone() @@ -1156,7 +1121,7 @@ impl Encodable for V2Transaction { fn encode(&self, encoder: &mut Encoder) { encoder.write_u64(self.siacoin_inputs.len() as u64); for si in &self.siacoin_inputs { - si.parent.state_element.id.encode(encoder); + si.parent.id.encode(encoder); } encoder.write_u64(self.siacoin_outputs.len() as u64); @@ -1166,7 +1131,7 @@ impl Encodable for V2Transaction { encoder.write_u64(self.siafund_inputs.len() as u64); for si in &self.siafund_inputs { - si.parent.state_element.id.encode(encoder); + si.parent.id.encode(encoder); } encoder.write_u64(self.siafund_outputs.len() as u64); @@ -1181,13 +1146,13 @@ impl Encodable for V2Transaction { encoder.write_u64(self.file_contract_revisions.len() as u64); for fcr in &self.file_contract_revisions { - fcr.parent.state_element.id.encode(encoder); + fcr.parent.id.encode(encoder); fcr.revision.with_nil_sigs().encode(encoder); } encoder.write_u64(self.file_contract_resolutions.len() as u64); for fcr in &self.file_contract_resolutions { - fcr.parent.state_element.id.encode(encoder); + fcr.parent.id.encode(encoder); fcr.with_nil_sigs().encode(encoder); // FIXME .encode() leads to unimplemented!() } @@ -1244,7 +1209,7 @@ impl Encodable for V2TransactionBuilder { fn encode(&self, encoder: &mut Encoder) { encoder.write_u64(self.siacoin_inputs.len() as u64); for si in &self.siacoin_inputs { - si.parent.state_element.id.encode(encoder); + si.parent.id.encode(encoder); } encoder.write_u64(self.siacoin_outputs.len() as u64); @@ -1254,7 +1219,7 @@ impl Encodable for V2TransactionBuilder { encoder.write_u64(self.siafund_inputs.len() as u64); for si in &self.siafund_inputs { - si.parent.state_element.id.encode(encoder); + si.parent.id.encode(encoder); } encoder.write_u64(self.siafund_outputs.len() as u64); @@ -1269,13 +1234,13 @@ impl Encodable for V2TransactionBuilder { encoder.write_u64(self.file_contract_revisions.len() as u64); for fcr in &self.file_contract_revisions { - fcr.parent.state_element.id.encode(encoder); + fcr.parent.id.encode(encoder); fcr.revision.with_nil_sigs().encode(encoder); } encoder.write_u64(self.file_contract_resolutions.len() as u64); for fcr in &self.file_contract_resolutions { - fcr.parent.state_element.id.encode(encoder); + fcr.parent.id.encode(encoder); fcr.with_nil_sigs().encode(encoder); // FIXME .encode() leads to unimplemented!() } From 4af0ccb3ad4a70c38cb554e7e39e4661dbfb80a9 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 22:10:14 -0500 Subject: [PATCH 197/233] change FileContractID from type alias to newtype pattern - this is not strictly needed but it better matches the Go implementation --- src/types/transaction.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 0f0c3d9..c8909b1 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -460,6 +460,14 @@ impl Encodable for SiafundOutputId { fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder) } } +#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display)] +#[serde(transparent)] +pub struct FileContractID(pub Hash256); + +impl Encodable for FileContractID { + fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder) } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct SiacoinOutput { pub value: Currency, @@ -715,8 +723,6 @@ impl Encodable for StorageProof { } } -type FileContractID = Hash256; - #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct FileContractRevision { pub parent_id: FileContractID, From 91ad54816d62dc4c2c2a6962495cd7f37811c44e Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 22:57:21 -0500 Subject: [PATCH 198/233] fix SiacoinElement encoding --- src/types/transaction.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index c8909b1..7107a5d 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -326,6 +326,7 @@ pub struct SiacoinElement { impl Encodable for SiacoinElement { fn encode(&self, encoder: &mut Encoder) { self.state_element.encode(encoder); + self.id.encode(encoder); SiacoinOutputVersion::V2(&self.siacoin_output).encode(encoder); encoder.write_u64(self.maturity_height); } From a2bfed079f27195b45aa9c64cebd46e5ce0fe70d Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 22:57:46 -0500 Subject: [PATCH 199/233] derive Default for FileContractID and SiacoinOutputId --- src/types/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 7107a5d..316227c 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -435,7 +435,7 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { /// more generic "h:" prefix. related: pub type TransactionId = Hash256; -#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display)] +#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display, Default)] #[serde(transparent)] pub struct SiacoinOutputId(pub Hash256); @@ -461,7 +461,7 @@ impl Encodable for SiafundOutputId { fn encode(&self, encoder: &mut Encoder) { self.0.encode(encoder) } } -#[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display)] +#[derive(Clone, Debug, Default, PartialEq, From, Into, Deserialize, Serialize, Display)] #[serde(transparent)] pub struct FileContractID(pub Hash256); From 2336121fde113ae8dcdd24902d989cd528fd66c5 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 22:58:58 -0500 Subject: [PATCH 200/233] fix various compilation errors in transaction unit tests related to StateElement consensus changes from Sia Go - removed id field from StateElement --- src/tests/transaction.rs | 47 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index e009033..2207002 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -85,14 +85,14 @@ mod test { fn test_siacoin_element_encode() { let state_element = StateElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, - merkle_proof: Some(vec![ + merkle_proof: vec![ Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), + ], }; let siacoin_element = SiacoinElement { + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, siacoin_output: SiacoinOutput { value: 1u64.into(), @@ -105,18 +105,17 @@ mod test { }; let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = Hash256::from_str("h:3c867a54b7b3de349c56585f25a4365f31d632c3e42561b615055c77464d889e").unwrap(); + let expected = Hash256::from_str("h:4c46cbe535099409d2ea4255debda3fb62993595e305c78688ec4306f8464d7d").unwrap(); assert_eq!(hash, expected); } fn test_state_element_encode() { let state_element = StateElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, - merkle_proof: Some(vec![ + merkle_proof: vec![ Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), + ], }; let hash = Encoder::encode_and_hash(&state_element); @@ -125,14 +124,20 @@ mod test { } fn test_state_element_encode_null_merkle_proof() { - let state_element = StateElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), - leaf_index: 1, - merkle_proof: None, - }; + let j = r#"{"leafIndex":1}"#; + let state_element = serde_json::from_str::(j).unwrap(); + + let hash = Encoder::encode_and_hash(&state_element); + let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + assert_eq!(hash, expected); + } + + fn test_state_element_encode_empty_merkle_proof() { + let j = r#"{"leafIndex":1,"merkleProof":[]}"#; + let state_element = serde_json::from_str::(j).unwrap(); let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::from_str("h:d69bc48bc797aff93050447aff0a3f7c4d489705378c122cd123841fe7778a3e").unwrap(); + let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -385,10 +390,10 @@ mod test { let vin = SiacoinInputV2 { parent: SiacoinElement { + id: SiacoinOutputId::default(), state_element: StateElement { - id: Hash256::default(), leaf_index: 0, - merkle_proof: Some(vec![Hash256::default()]), + merkle_proof: vec![Hash256::default()], }, siacoin_output: SiacoinOutput { value: 1u64.into(), @@ -517,15 +522,15 @@ mod test { }; let state_element = StateElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, - merkle_proof: Some(vec![ + merkle_proof: vec![ Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), + ], }; let file_contract_element_v2 = V2FileContractElement { + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, v2_file_contract: file_contract_v2, }; @@ -579,15 +584,15 @@ mod test { }; let state_element = StateElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(), leaf_index: 1, - merkle_proof: Some(vec![ + merkle_proof: vec![ Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), - ]), + ], }; let file_contract_element_v2 = V2FileContractElement { + id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, v2_file_contract: file_contract_v2.clone(), }; From fd326ecd924e336935660c164c6dfe3a7d54fc66 Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 23:35:52 -0500 Subject: [PATCH 201/233] fix SiacoinElement id field serde --- src/types/transaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 316227c..df23dfc 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -316,7 +316,6 @@ impl Encodable for SiafundElement { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SiacoinElement { - #[serde(rename = "ID")] pub id: SiacoinOutputId, pub state_element: StateElement, pub siacoin_output: SiacoinOutput, From e8879c7b46b39d52ecdcf04bc495bbfa48291bdc Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 23:36:35 -0500 Subject: [PATCH 202/233] add capacity field to V2FileContract fix V2FileContractElement encoding --- src/types/transaction.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index df23dfc..f0dacc1 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -589,6 +589,7 @@ impl Encodable for FileContract { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct V2FileContract { + pub capacity: u64, pub filesize: u64, pub file_merkle_root: Hash256, pub proof_height: u64, @@ -616,6 +617,7 @@ impl V2FileContract { impl Encodable for V2FileContract { fn encode(&self, encoder: &mut Encoder) { + encoder.write_u64(self.capacity); encoder.write_u64(self.filesize); self.file_merkle_root.encode(encoder); encoder.write_u64(self.proof_height); @@ -643,6 +645,7 @@ pub struct V2FileContractElement { impl Encodable for V2FileContractElement { fn encode(&self, encoder: &mut Encoder) { self.state_element.encode(encoder); + self.id.encode(encoder); self.v2_file_contract.encode(encoder); } } From 4cee4a1105dc0c6bd2f05ab81d7e481b8d11528b Mon Sep 17 00:00:00 2001 From: Alright Date: Thu, 14 Nov 2024 23:37:10 -0500 Subject: [PATCH 203/233] fix various transaction unit tests corresponding to rust_port_test.go inside komodoplatform/sia-core repo --- src/tests/transaction.rs | 69 +++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 2207002..77cca67 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -119,7 +119,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::from_str("h:bf6d7b74fb1e15ec4e86332b628a450e387c45b54ea98e57a6da8c9af317e468").unwrap(); + let expected = Hash256::from_str("h:70f868873fcb6196cd54bbb1e9e480188043426d3f7c9dc8fc5a7a536981cef1").unwrap(); assert_eq!(hash, expected); } @@ -212,8 +212,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); + let expected = Hash256::from_str("h:80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); assert_eq!(hash, expected); } @@ -229,8 +228,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:af7df9041212334ae04b007035e862449ddcb3c1a007da2bd609f65f0392f99a").unwrap(); + let expected = Hash256::from_str("h:80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); assert_eq!(hash, expected); } @@ -310,7 +308,7 @@ mod test { let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:d7270a87868f9127bf99cc33a3548b669afb308c49760c840d7a15e8066f8c47").unwrap(); + let expected = Hash256::from_str("h:2200a1464864cfaea8d312c1f16b5e00b816110896bea32ef7e1ccd43042d312").unwrap(); assert_eq!(hash, expected); } @@ -340,7 +338,7 @@ mod test { let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:4da166c66b22c6cc825253d4e9b4f5319549b82ade6f9c8a037d8e7a4acfcdfa").unwrap(); + let expected = Hash256::from_str("h:08852e4ad99f726120028ecd82925b5f55fa441952cfc034a5cf4f09159b9372").unwrap(); assert_eq!(hash, expected); } @@ -406,7 +404,7 @@ mod test { let hash = Encoder::encode_and_hash(&vin); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:10497f5864991eb72c2bc49ad61a5afdd068bb48dca9db825b5adb94b49b9cbe").unwrap(); + let expected = Hash256::from_str("h:d31a05b155113a5244f14ae833887fd8b30f555129be126ca4b90592290db24a").unwrap(); assert_eq!(hash, expected); } @@ -458,6 +456,7 @@ mod test { }; let file_contract_v2 = V2FileContract { + capacity: 0, filesize: 1, file_merkle_root: Hash256::default(), proof_height: 1, @@ -474,7 +473,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = Hash256::from_str("h:6171a8d8ec31e06f80d46efbd1aecf2c5a7c344b5f2a2d4f660654b0cb84113c").unwrap(); + let expected = Hash256::from_str("h:e851362bab643dc066b9d3c22c0fa0d67bc7b0cb520c689765e2292f4e7f435e").unwrap(); assert_eq!(hash, expected); } @@ -506,6 +505,7 @@ mod test { }; let file_contract_v2 = V2FileContract { + capacity: 0, filesize: 1, file_merkle_root: Hash256::default(), proof_height: 1, @@ -530,13 +530,13 @@ mod test { }; let file_contract_element_v2 = V2FileContractElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), + id: Hash256::from_str("h:0707070000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, v2_file_contract: file_contract_v2, }; let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = Hash256::from_str("h:4cde411635118b2b7e1b019c659a2327ada53b303da0e46524e604d228fcd039").unwrap(); + let expected = Hash256::from_str("h:3005594b14c1615aadaef2d8558713ebeabfa7d54f1dec671ba67ea8264816e6").unwrap(); assert_eq!(hash, expected); } @@ -568,6 +568,7 @@ mod test { }; let file_contract_v2 = V2FileContract { + capacity: 0, filesize: 1, file_merkle_root: Hash256::default(), proof_height: 1, @@ -603,7 +604,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = Hash256::from_str("h:22d5d1fd8c2762758f6b6ecf7058d73524ef209ac5a64f160b71ce91677db9a6").unwrap(); + let expected = Hash256::from_str("h:4f23582ec40570345f72adab8cd6249c0167669b78aec9ac7209befefc281f4f").unwrap(); assert_eq!(hash, expected); } @@ -614,15 +615,17 @@ mod test { { "parent": { "id": "h:b49cba94064a92a75bf8c6f9d32ab18f38bfb14a2252e3e117d04da89d536f29", - "leafIndex": 302, - "merkleProof": [ - "h:6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", - "h:eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", - "h:04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", - "h:16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", - "h:1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", - "h:31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" - ], + "stateElement": { + "leafIndex": 302, + "merkleProof": [ + "h:6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", + "h:eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", + "h:04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", + "h:16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", + "h:1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", + "h:31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" + ], + }, "siacoinOutput": { "value": "288594172736732570239334030000", "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" @@ -673,17 +676,19 @@ mod test { { "parent": { "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", - "leafIndex": 3, - "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" - ], + "stateElement": { + "leafIndex": 3, + "merkleProof": [ + "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + ] + }, "siacoinOutput": { "value": "300000000000000000000000000000", "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" From 1224470498c84ebe8ac294f28179712caa03e990 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 12:13:12 -0500 Subject: [PATCH 204/233] refactor Hash256 serde to account for Sia Go SiaFoundation/core/pull/224 Prior to this change Hash256 was represented as "h:<32 byte hex string>" It is now represented as a plain hex string with no prefix --- Cargo.lock | 3 + Cargo.toml | 2 +- src/transport/endpoints.rs | 2 +- src/types.rs | 6 +- src/types/hash.rs | 193 ++++++++----------------------------- 5 files changed, 49 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 976ea54..e89142c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "http" diff --git a/Cargo.toml b/Cargo.toml index 2fd2cc2..4eef62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ nom = "6.1.2" blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } log = { version = "0.4.19", "features" = ["std"] } -hex = "0.4.2" +hex = { version = "0.4.2", "features" = ["serde"] } reqwest = { version = "0.11.9", features = ["json", "rustls-tls"], default-features = false } base64 = "0.21.2" url = { version = "2.2.2", features = ["serde"] } diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 343b6ef..469355e 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -165,7 +165,7 @@ impl SiaApiRequest for ConsensusUpdatesRequest { // Create the path_params HashMap to substitute {height} and {hash} in the path schema let mut path_params = HashMap::new(); path_params.insert("height".to_owned(), self.height.to_string()); - path_params.insert("hash".to_owned(), format!("{:02x}", self.block_hash.0)); + path_params.insert("hash".to_owned(), format!("{}", self.block_hash.0)); let mut query_params = HashMap::new(); if let Some(limit) = self.limit { diff --git a/src/types.rs b/src/types.rs index 01d1212..b04a8ea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,7 +11,7 @@ use std::fmt; use std::str::FromStr; mod hash; -pub use hash::{Hash256, ParseHashError}; +pub use hash::{Hash256, Hash256Error}; mod signature; pub use signature::{Signature, SignatureError}; @@ -184,7 +184,7 @@ impl<'de> Deserialize<'de> for BlockID { E: serde::de::Error, { if let Some(hex_str) = value.strip_prefix("bid:") { - Hash256::from_str_no_prefix(hex_str) + Hash256::from_str(hex_str) .map(BlockID) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { @@ -207,7 +207,7 @@ impl Serialize for BlockID { } impl fmt::Display for BlockID { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "bid:{:02x}", self.0) } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] diff --git a/src/types/hash.rs b/src/types/hash.rs index 250f70c..03f992c 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -1,110 +1,45 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use hex; +use serde::{Deserialize, Serialize}; use std::convert::TryFrom; -use std::fmt; +use std::fmt::{self, Display}; use std::str::FromStr; use thiserror::Error; #[derive(Debug, Error)] -pub enum ParseHashError { - #[error("Hash256 invalid prefix: expected 32 byte hex string prefixed with 'h:', found {0}")] - InvalidPrefix(String), - #[error("Hash256 invalid hex: expected 32 byte hex string prefixed with 'h:', found {0}")] +pub enum Hash256Error { + #[error("Hash256::from_str invalid hex: expected 32 byte hex string, found {0}")] InvalidHex(String), - #[error("Hash256 invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] + #[error("Hash256::from_str invalid length: expected 32 byte hex string, found {0}")] InvalidLength(String), - #[error("Hash256 invalid slice length: expected 32 byte slice, found {0} byte slice")] - InvalidSliceLength(usize), + #[error("Hash256::TryFrom<&[u8]> invalid slice length: expected 32 byte slice, found {0:?}")] + InvalidSliceLength(Vec), } -#[derive(Clone, Eq, PartialEq)] -pub struct Hash256(pub [u8; 32]); +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Hash256(#[serde(with = "hex::serde")] pub [u8; 32]); -impl Hash256 { - const fn const_default() -> Hash256 { Hash256([0; 32]) } +impl FromStr for Hash256 { + type Err = Hash256Error; - // Method for parsing a hex string without the "h:" prefix - pub fn from_str_no_prefix(hex_str: &str) -> Result { + fn from_str(hex_str: &str) -> Result { if hex_str.len() != 64 { - return Err(ParseHashError::InvalidLength(hex_str.to_string())); + return Err(Hash256Error::InvalidLength(hex_str.to_string())); } let mut bytes = [0u8; 32]; match hex::decode_to_slice(hex_str, &mut bytes) { Ok(_) => Ok(Hash256(bytes)), - Err(_) => Err(ParseHashError::InvalidHex(hex_str.to_string())), - } - } -} - -impl Default for Hash256 { - fn default() -> Self { Hash256::const_default() } -} - -impl fmt::Display for Hash256 { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "h:{:02x}", self) } -} - -impl fmt::Debug for Hash256 { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } -} - -impl fmt::LowerHex for Hash256 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - for byte in &self.0 { - write!(f, "{:02x}", byte)?; + Err(_) => Err(Hash256Error::InvalidHex(hex_str.to_string())), } - Ok(()) } } -impl Serialize for Hash256 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for Hash256 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct H256Visitor; - - impl<'de> serde::de::Visitor<'de> for H256Visitor { - type Value = Hash256; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'h:' and followed by a 32 byte hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Hash256::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - - deserializer.deserialize_str(H256Visitor) - } -} - -impl FromStr for Hash256 { - type Err = ParseHashError; - - fn from_str(value: &str) -> Result { - if let Some(hex_str) = value.strip_prefix("h:") { - Hash256::from_str_no_prefix(hex_str) - } else { - Err(ParseHashError::InvalidPrefix(value.to_string())) - } - } +impl Display for Hash256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(&self.0)) } } impl TryFrom<&[u8]> for Hash256 { - type Error = ParseHashError; + type Error = Hash256Error; fn try_from(slice: &[u8]) -> Result { let slice_len = slice.len(); @@ -113,7 +48,7 @@ impl TryFrom<&[u8]> for Hash256 { array.copy_from_slice(slice); Ok(Hash256(array)) } else { - Err(ParseHashError::InvalidSliceLength(slice_len)) + Err(Hash256Error::InvalidSliceLength(slice.to_owned())) } } } @@ -125,115 +60,69 @@ mod tests { cross_target_tests! { fn test_default() { - let hash = Hash256::from_str("h:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()); } fn test_valid() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_display() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - } - - fn test_debug() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(format!("{:?}", hash), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_serialize() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); let serialized = serde_json::to_string(&hash).unwrap(); - assert_eq!(&serialized, r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); + assert_eq!(&serialized, r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); } fn test_deserialize() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - let deserialized: Hash256 = serde_json::from_str(r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let deserialized: Hash256 = serde_json::from_str(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); assert_eq!(deserialized, hash); } - fn test_deserialize_missing_prefix() { - let err = serde_json::from_str::(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).expect_err("no prefix"); - assert!(format!("{:?}", err).contains("expected a string prefixed with 'h:' and followed by a 32 byte hex string")); - } - - fn test_missing_prefix() { - let test_case = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("no prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_corrupt_prefix() { - let test_case = ":c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("no prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_wrong_prefix() { - let test_case = "i:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("wrong prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - fn test_invalid_hex() { - let err = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); + let err = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); let expected = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg"; match err { - ParseHashError::InvalidHex(ref e) if expected == e => (), + Hash256Error::InvalidHex(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } fn test_invalid_length() { - let err = Hash256::from_str("h:badc0de").expect_err("invalid length"); + let err = Hash256::from_str("badc0de").expect_err("invalid length"); let expected = "badc0de"; match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidLength(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } - fn test_from_str_no_prefix_valid() { - let hash = Hash256::from_str_no_prefix("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + fn test_from_str_valid() { + let hash = Hash256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()) } - fn test_from_str_no_prefix_invalid_length() { - let err = Hash256::from_str_no_prefix("badc0de").expect_err("invalid length"); + fn test_from_str_invalid_length() { + let err = Hash256::from_str("badc0de").expect_err("invalid length"); let expected = "badc0de"; match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidLength(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } - fn test_from_str_no_prefix_invalid_hex() { - let err = Hash256::from_str_no_prefix("g00000000000000000000000000000000000000000000000000000000000000e").expect_err("invalid hex"); + fn test_from_str_invalid_hex() { + let err = Hash256::from_str("g00000000000000000000000000000000000000000000000000000000000000e").expect_err("invalid hex"); let expected = "g00000000000000000000000000000000000000000000000000000000000000e"; match err { - ParseHashError::InvalidHex(ref e) if expected == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_from_str_no_prefix_invalid_has_prefix() { - let err = Hash256::from_str_no_prefix("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").expect_err("invalid hex"); - let expected = "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidHex(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } From b75361eb50a2296db7abf4e05e11763420d8fc34 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 12:22:19 -0500 Subject: [PATCH 205/233] remove now irrelevant dev comment --- src/types/transaction.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index f0dacc1..320073a 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -428,10 +428,8 @@ impl<'a> Encodable for SiacoinOutputVersion<'a> { } /// A Sia transaction id aka "txid" -/// This could be a newtype like SiacoinOutputId with custom serde, but we have no use for this beyond -/// making SiacoinOutputId::new more explicit. -/// Sia Go uses "txid:" prefix for this type, but walletd API generally represents this with the -/// more generic "h:" prefix. related: +// This could be a newtype like SiacoinOutputId with custom serde, but we have no use for this beyond +// making SiacoinOutputId::new more explicit. pub type TransactionId = Hash256; #[derive(Clone, Debug, PartialEq, From, Into, Deserialize, Serialize, Display, Default)] From 26573003ebe8713b577a88cca38f6ffc0296f4d3 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 12:22:45 -0500 Subject: [PATCH 206/233] remove h: prefix from all unit tests --- src/blake2b_internal.rs | 16 ++--- src/encoding.rs | 18 ++--- src/tests/encoding.rs | 22 +++--- src/tests/serde.rs | 118 ++++++++++++++++---------------- src/tests/spend_policy.rs | 8 +-- src/tests/transaction.rs | 120 ++++++++++++++++----------------- src/transport/client/native.rs | 2 +- src/transport/client/wasm.rs | 16 ++--- 8 files changed, 160 insertions(+), 160 deletions(-) diff --git a/src/blake2b_internal.rs b/src/blake2b_internal.rs index a8df535..382296c 100644 --- a/src/blake2b_internal.rs +++ b/src/blake2b_internal.rs @@ -185,7 +185,7 @@ mod test { let sigs_required_leaf = sigs_required_leaf(1u64); accumulator.add_leaf(sigs_required_leaf); - let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(accumulator.root(), expected); } @@ -206,7 +206,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(root, expected) } @@ -234,7 +234,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::from_str("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::from_str("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(root, expected) } @@ -262,7 +262,7 @@ mod test { accumulator.add_leaf(sigs_required_leaf); let root = accumulator.root(); - let expected = Hash256::from_str("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::from_str("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(root, expected) } @@ -273,7 +273,7 @@ mod test { .unwrap(); let hash = standard_unlock_hash(&pubkey); - let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -288,7 +288,7 @@ mod test { .unwrap(); let hash = hash_blake2b_pair(&NODE_HASH_PREFIX, &left, &right); - let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected) } @@ -306,7 +306,7 @@ mod test { fn test_hash_blake2b_single() { let hash = hash_blake2b_single(&hex::decode("006564323535313900000000000000000020000000000000000102030000000000000000000000000000000000000000000000000000000000").unwrap()); - let expected = Hash256::from_str("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::from_str("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } @@ -317,7 +317,7 @@ mod test { .unwrap(); let hash = public_key_leaf(&UnlockKey::Ed25519(pubkey)); - let expected = Hash256::from_str("h:21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); + let expected = Hash256::from_str("21ce940603a2ee3a283685f6bfb4b122254894fd1ed3eb59434aadbf00c75d5b").unwrap(); assert_eq!(hash, expected) } } diff --git a/src/encoding.rs b/src/encoding.rs index d6bdf7e..e27dd94 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -66,7 +66,7 @@ mod test { fn test_encoder_default_hash() { assert_eq!( Encoder::default().hash(), - Hash256::from_str("h:0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + Hash256::from_str("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() ) } @@ -75,7 +75,7 @@ mod test { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - Hash256::from_str("h:d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() + Hash256::from_str("d4a72b52e2e1f40e20ee40ea6d5080a1b1f76164786defbb7691a4427f3388f5").unwrap() ); } @@ -84,7 +84,7 @@ mod test { encoder.write_u8(1); assert_eq!( encoder.hash(), - Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -93,7 +93,7 @@ mod test { encoder.write_u64(1); assert_eq!( encoder.hash(), - Hash256::from_str("h:1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() + Hash256::from_str("1dbd7d0b561a41d23c2a469ad42fbd70d5438bae826f6fd607413190c37c363b").unwrap() ); } @@ -102,7 +102,7 @@ mod test { encoder.write_distinguisher("test"); assert_eq!( encoder.hash(), - Hash256::from_str("h:25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() + Hash256::from_str("25fb524721bf98a9a1233a53c40e7e198971b003bf23c24f59d547a1bb837f9c").unwrap() ); } @@ -111,7 +111,7 @@ mod test { encoder.write_bool(true); assert_eq!( encoder.hash(), - Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); } @@ -120,14 +120,14 @@ mod test { encoder.write_bool(true); assert_eq!( encoder.hash(), - Hash256::from_str("h:ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() + Hash256::from_str("ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25").unwrap() ); encoder.reset(); encoder.write_bool(false); assert_eq!( encoder.hash(), - Hash256::from_str("h:03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() + Hash256::from_str("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").unwrap() ); } @@ -139,7 +139,7 @@ mod test { encoder.write_len_prefixed_bytes(&[1, 2, 3, 4]); assert_eq!( encoder.hash(), - Hash256::from_str("h:b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() + Hash256::from_str("b66d7a9bef9fb303fe0e41f6b5c5af410303e428c4ff9231f6eb381248693221").unwrap() ); } } diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index d825dc3..1f2f090 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -18,7 +18,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 2); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::from_str("h:1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); + let expected = Hash256::from_str("1e94357817d236167e54970a8c08bbd41b37bfceeeb52f6c1ce6dd01d50ea1e7").unwrap(); assert_eq!(hash, expected); } @@ -34,7 +34,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey, pubkey2], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::from_str("h:d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); + let expected = Hash256::from_str("d7f84e3423da09d111a17f64290c8d05e1cbe4cab2b6bed49e3a4d2f659f0585").unwrap(); assert_eq!(hash, expected); } @@ -42,7 +42,7 @@ mod test { let policy = SpendPolicy::above(1); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::from_str("h:bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); + let expected = Hash256::from_str("bebf6cbdfb440a92e3e5d832ac30fe5d226ff6b352ed3a9398b7d35f086a8ab6").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -54,7 +54,7 @@ mod test { fn test_spend_policy_encode_after() { let policy = SpendPolicy::after(1); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::from_str("h:07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); + let expected = Hash256::from_str("07b0f28eafd87a082ad11dc4724e1c491821260821a30bec68254444f97d9311").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -71,7 +71,7 @@ mod test { let policy = SpendPolicy::PublicKey(pubkey); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::from_str("h:4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); + let expected = Hash256::from_str("4355c8f80f6e5a98b70c9c2f9a22f17747989b4744783c90439b2b034f698bfe").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -81,11 +81,11 @@ mod test { } fn test_spend_policy_encode_hash() { - let hash = Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::Hash(hash); let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::from_str("h:9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); + let expected = Hash256::from_str("9938967aefa6cbecc1f1620d2df5170d6811d4b2f47a879b621c1099a3b0628a").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -101,7 +101,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&policy); - let expected = Hash256::from_str("h:7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); + let expected = Hash256::from_str("7d792df6cd0b5e0f795287b3bf4087bbcc4c1bd0c52880a552cdda3e5e33d802").unwrap(); assert_eq!(hash, expected); let address = policy.address(); @@ -141,7 +141,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey], 0, 1); let hash = Encoder::encode_and_hash(&unlock_condition); - let expected = Hash256::from_str("h:5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); + let expected = Hash256::from_str("5d49bae37b97c86573a1525246270c180464acf33d63cc2ac0269ef9a8cb9d98").unwrap(); assert_eq!(hash, expected); } @@ -152,7 +152,7 @@ mod test { .unwrap(); let hash = Encoder::encode_and_hash(&public_key); - let expected = Hash256::from_str("h:d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); + let expected = Hash256::from_str("d487326614f066416308bf6aa4e5041d1949928e4b26ede98e3cebb36a3b1726").unwrap(); assert_eq!(hash, expected); } @@ -164,7 +164,7 @@ mod test { let unlock_condition = UnlockCondition::new(vec![pubkey.clone()], 0, 1); let hash = unlock_condition.unlock_hash(); - let expected = Hash256::from_str("h:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); + let expected = Hash256::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515d").unwrap(); assert_eq!(hash, expected); let hash = standard_unlock_hash(&pubkey); assert_eq!(hash, expected); diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b140ab9..bc7171e 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -18,7 +18,7 @@ fn test_serde_event_v2_contract_resolution_finalization() { use crate::types::Event; let j = json!( { - "id": "h:4057e021e1d6dec8d4e4ef9d6e9fa2e4491c559144848b9af5765e03b39bb69d", + "id": "4057e021e1d6dec8d4e4ef9d6e9fa2e4491c559144848b9af5765e03b39bb69d", "index": { "height": 0, "id": "bid:0000000000000000000000000000000000000000000000000000000000000000" @@ -28,18 +28,18 @@ fn test_serde_event_v2_contract_resolution_finalization() { "type": "v2ContractResolution", "data": { "parent": { - "id": "h:ee87ab83f9d16c9377d6154c477ac40d2ee70619de2ba146fcfe36fd0de86bf5", + "id": "ee87ab83f9d16c9377d6154c477ac40d2ee70619de2ba146fcfe36fd0de86bf5", "leafIndex": 6680213938505633000u64, "merkleProof": [ - "h:0000000000000000000000000000000000000000000000000000000000000000", - "h:0000000000000000000000000000000000000000000000000000000000000000", - "h:0000000000000000000000000000000000000000000000000000000000000000", - "h:0000000000000000000000000000000000000000000000000000000000000000", - "h:0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000" ], "v2FileContract": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 10, "expirationHeight": 20, "renterOutput": { @@ -62,7 +62,7 @@ fn test_serde_event_v2_contract_resolution_finalization() { "type": "finalization", "resolution": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 10, "expirationHeight": 20, "renterOutput": { @@ -122,7 +122,7 @@ mod test { fn test_serde_sia_hash() { test_serde!( Hash256, - json!("h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") + json!("dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1") ); } @@ -136,7 +136,7 @@ mod test { fn test_serde_state_element() { let j = json!({ - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", "leafIndex": 21, "merkleProof": null }); @@ -145,9 +145,9 @@ mod test { fn test_serde_siacoin_element() { let j = json!( { - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", "leafIndex": 21, - "merkleProof": ["h:8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], + "merkleProof": ["8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], "siacoinOutput": { "value": "300000000000000000000000000000", "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" @@ -160,7 +160,7 @@ mod test { fn test_serde_siacoin_element_null_merkle_proof() { let j = json!( { - "id": "h:dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", "leafIndex": 21, "merkleProof": null, "siacoinOutput": { @@ -176,7 +176,7 @@ mod test { fn test_serde_event_v2_contract_resolution_storage_proof() { let j = json!( { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "id": "a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", "index": { "height": 201, "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" @@ -187,18 +187,18 @@ mod test { "data": { "resolution": { "parent": { - "id": "h:b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", + "id": "b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", "leafIndex": 397, "merkleProof": [ - "h:4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", - "h:e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", - "h:0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", - "h:37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", - "h:077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" + "4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", + "e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", + "0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", + "37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", + "077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" ], "v2FileContract": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 200, "expirationHeight": 210, "renterOutput": { @@ -221,7 +221,7 @@ mod test { "type": "storageProof", "resolution": { "proofIndex": { - "id": "h:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", + "id": "ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", "leafIndex": 416, "merkleProof": [], "chainIndex": { @@ -234,7 +234,7 @@ mod test { } }, "siacoinElement": { - "id": "h:a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "id": "a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", "leafIndex": 418, "merkleProof": null, "siacoinOutput": { @@ -256,7 +256,7 @@ mod test { fn test_serde_event_v2_contract_resolution_renewal() { let j = json!( { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "id": "debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", "index": { "height": 203, "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" @@ -267,17 +267,17 @@ mod test { "data": { "resolution": { "parent": { - "id": "h:06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", + "id": "06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", "leafIndex": 423, "merkleProof": [ - "h:ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", - "h:147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", - "h:1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" + "ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", + "147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", + "f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", + "1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" ], "v2FileContract": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 211, "expirationHeight": 221, "renterOutput": { @@ -301,7 +301,7 @@ mod test { "resolution": { "finalRevision": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 211, "expirationHeight": 221, "renterOutput": { @@ -322,7 +322,7 @@ mod test { }, "newContract": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 221, "expirationHeight": 231, "renterOutput": { @@ -348,7 +348,7 @@ mod test { } }, "siacoinElement": { - "id": "h:debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "id": "debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", "leafIndex": 427, "merkleProof": null, "siacoinOutput": { @@ -371,7 +371,7 @@ mod test { fn test_serde_event_v2_contract_resolution_expiration() { let j = json!( { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "id": "4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", "index": { "height": 190, "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" @@ -382,20 +382,20 @@ mod test { "data": { "resolution": { "parent": { - "id": "h:34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", + "id": "34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", "leafIndex": 351, "merkleProof": [ - "h:e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", - "h:70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", - "h:42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", - "h:4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", - "h:59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", - "h:f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", - "h:3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" + "e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", + "70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", + "42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", + "4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", + "59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", + "f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", + "3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" ], "v2FileContract": { "filesize": 0, - "fileMerkleRoot": "h:0000000000000000000000000000000000000000000000000000000000000000", + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 179, "expirationHeight": 189, "renterOutput": { @@ -419,7 +419,7 @@ mod test { "resolution": {} }, "siacoinElement": { - "id": "h:4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "id": "4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", "leafIndex": 391, "merkleProof": null, "siacoinOutput": { @@ -439,7 +439,7 @@ mod test { fn test_serde_event_v2_transaction() { let j = json!( { - "id": "h:5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", + "id": "5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", "index": { "height": 203, "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" @@ -451,12 +451,12 @@ mod test { "siacoinInputs": [ { "parent": { - "id": "h:78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", + "id": "78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", "leafIndex": 421, "merkleProof": [ - "h:f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", - "h:d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", - "h:f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" + "f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", + "d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", + "f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" ], "siacoinOutput": { "value": "256394172736732570239334030000", @@ -504,17 +504,17 @@ mod test { "siacoinInputs": [ { "parent": { - "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", + "id": "f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", "leafIndex": 3, "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + "ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" ], "siacoinOutput": { "value": "300000000000000000000000000000", diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index bc9d704..3c5b129 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -53,10 +53,10 @@ mod test { let j = json!( { "type": "h", - "policy": "h:0102030000000000000000000000000000000000000000000000000000000000" + "policy": "0102030000000000000000000000000000000000000000000000000000000000" } ); - let hash = Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("0102030000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Hash(hash); @@ -88,7 +88,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let spend_policy = SpendPolicy::atomic_swap_success(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let j = json!( @@ -108,7 +108,7 @@ mod test { }, { "type": "h", - "policy": "h:0100000000000000000000000000000000000000000000000000000000000000" + "policy": "0100000000000000000000000000000000000000000000000000000000000000" } ] } diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 77cca67..94488de 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -16,14 +16,14 @@ mod test { let unlock_condition = UnlockCondition::new(vec![public_key], 0, 1); let vin = SiacoinInputV1 { - parent_id: Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000") + parent_id: Hash256::from_str("0405060000000000000000000000000000000000000000000000000000000000") .unwrap() .into(), unlock_condition, }; let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::from_str("h:1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); + let expected = Hash256::from_str("1d4b77aaa82c71ca68843210679b380f9638f8bec7addf0af16a6536dd54d6b4").unwrap(); assert_eq!(hash, expected); } @@ -31,7 +31,7 @@ mod test { let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::from_str("h:a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); + let expected = Hash256::from_str("a1cc3a97fc1ebfa23b0b128b153a29ad9f918585d1d8a32354f547d8451b7826").unwrap(); assert_eq!(hash, expected); } @@ -39,7 +39,7 @@ mod test { let currency: Currency = 1u64.into(); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + let expected = Hash256::from_str("a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -47,7 +47,7 @@ mod test { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V1(¤cy)); - let expected = Hash256::from_str("h:4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); + let expected = Hash256::from_str("4b9ed7269cb15f71ddf7238172a593a8e7ffe68b12c1bf73d67ac8eec44355bb").unwrap(); assert_eq!(hash, expected); } @@ -55,7 +55,7 @@ mod test { let currency = Currency(u128::MAX); let hash = Encoder::encode_and_hash(&CurrencyVersion::V2(¤cy)); - let expected = Hash256::from_str("h:681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); + let expected = Hash256::from_str("681467b3337425fd38fa3983531ca1a6214de9264eebabdf9c9bc5d157d202b4").unwrap(); assert_eq!(hash, expected); } @@ -67,7 +67,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V1(&vout)); - let expected = Hash256::from_str("h:3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); + let expected = Hash256::from_str("3253c57e76600721f2bdf03497a71ed47c09981e22ef49aed92e40da1ea91b28").unwrap(); assert_eq!(hash, expected); } @@ -79,7 +79,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&SiacoinOutputVersion::V2(&vout)); - let expected = Hash256::from_str("h:c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); + let expected = Hash256::from_str("c278eceae42f594f5f4ca52c8a84b749146d08af214cc959ed2aaaa916eaafd3").unwrap(); assert_eq!(hash, expected); } @@ -87,12 +87,12 @@ mod test { let state_element = StateElement { leaf_index: 1, merkle_proof: vec![ - Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ], }; let siacoin_element = SiacoinElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), + id: Hash256::from_str("0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, siacoin_output: SiacoinOutput { value: 1u64.into(), @@ -105,7 +105,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&siacoin_element); - let expected = Hash256::from_str("h:4c46cbe535099409d2ea4255debda3fb62993595e305c78688ec4306f8464d7d").unwrap(); + let expected = Hash256::from_str("4c46cbe535099409d2ea4255debda3fb62993595e305c78688ec4306f8464d7d").unwrap(); assert_eq!(hash, expected); } @@ -113,13 +113,13 @@ mod test { let state_element = StateElement { leaf_index: 1, merkle_proof: vec![ - Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ], }; let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::from_str("h:70f868873fcb6196cd54bbb1e9e480188043426d3f7c9dc8fc5a7a536981cef1").unwrap(); + let expected = Hash256::from_str("70f868873fcb6196cd54bbb1e9e480188043426d3f7c9dc8fc5a7a536981cef1").unwrap(); assert_eq!(hash, expected); } @@ -128,7 +128,7 @@ mod test { let state_element = serde_json::from_str::(j).unwrap(); let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + let expected = Hash256::from_str("a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -137,7 +137,7 @@ mod test { let state_element = serde_json::from_str::(j).unwrap(); let hash = Encoder::encode_and_hash(&state_element); - let expected = Hash256::from_str("h:a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); + let expected = Hash256::from_str("a3865e5e284e12e0ea418e73127db5d1092bfb98ed372ca9a664504816375e1d").unwrap(); assert_eq!(hash, expected); } @@ -148,7 +148,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&vin); - let expected = Hash256::from_str("h:2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); + let expected = Hash256::from_str("2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0").unwrap(); assert_eq!(hash, expected); } @@ -157,7 +157,7 @@ mod test { &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let hash = Encoder::encode_and_hash(&signature); - let expected = Hash256::from_str("h:1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); + let expected = Hash256::from_str("1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); assert_eq!(hash, expected); } @@ -179,7 +179,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); + let expected = Hash256::from_str("51832be911c7382502a2011cbddf1a9f689c4ca08c6a83ae3d021fb0dc781822").unwrap(); assert_eq!(hash, expected); } @@ -193,7 +193,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:1e612d1ee36338b93a36bac0c52007a2d678cde0bd9b95c36a1f61166cf02b87").unwrap(); + let expected = Hash256::from_str("1e612d1ee36338b93a36bac0c52007a2d678cde0bd9b95c36a1f61166cf02b87").unwrap(); assert_eq!(hash, expected); } @@ -212,7 +212,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); + let expected = Hash256::from_str("80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); assert_eq!(hash, expected); } @@ -228,7 +228,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); + let expected = Hash256::from_str("80f3caa4507615945bc839c8505546decd91e9642120f26938b2fc370fa61992").unwrap(); assert_eq!(hash, expected); } @@ -252,7 +252,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); + let expected = Hash256::from_str("c749f9ac53395ec557aed7e21d202f76a58e0de79222e5756b27077e9295931f").unwrap(); assert_eq!(hash, expected); } @@ -288,7 +288,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); + let expected = Hash256::from_str("13806b6c13a97478e476e0e5a0469c9d0ad8bf286bec0ada992e363e9fc60901").unwrap(); assert_eq!(hash, expected); } @@ -308,7 +308,7 @@ mod test { let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:2200a1464864cfaea8d312c1f16b5e00b816110896bea32ef7e1ccd43042d312").unwrap(); + let expected = Hash256::from_str("2200a1464864cfaea8d312c1f16b5e00b816110896bea32ef7e1ccd43042d312").unwrap(); assert_eq!(hash, expected); } @@ -322,7 +322,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_success(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let signature = Signature::from_bytes( @@ -338,7 +338,7 @@ mod test { let hash = Encoder::encode_and_hash(&satisfied_policy); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:08852e4ad99f726120028ecd82925b5f55fa441952cfc034a5cf4f09159b9372").unwrap(); + let expected = Hash256::from_str("08852e4ad99f726120028ecd82925b5f55fa441952cfc034a5cf4f09159b9372").unwrap(); assert_eq!(hash, expected); } @@ -352,7 +352,7 @@ mod test { ) .unwrap(); - let secret_hash = Hash256::from_str("h:0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let secret_hash = Hash256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_refund(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); let signature = Signature::from_bytes( @@ -367,7 +367,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&satisfied_policy); - let expected = Hash256::from_str("h:8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); + let expected = Hash256::from_str("8975e8cf990d5a20d9ec3dae18ed3b3a0c92edf967a8d93fcdef6a1eb73bb348").unwrap(); assert_eq!(hash, expected); } @@ -404,7 +404,7 @@ mod test { let hash = Encoder::encode_and_hash(&vin); // FIXME update this in go equivalent. Preimage was changed from Vec to [u8; 32] - let expected = Hash256::from_str("h:d31a05b155113a5244f14ae833887fd8b30f555129be126ca4b90592290db24a").unwrap(); + let expected = Hash256::from_str("d31a05b155113a5244f14ae833887fd8b30f555129be126ca4b90592290db24a").unwrap(); assert_eq!(hash, expected); } @@ -424,7 +424,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&attestation); - let expected = Hash256::from_str("h:b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); + let expected = Hash256::from_str("b28b32c6f91d1b57ab4a9ea9feecca16b35bb8febdee6a0162b22979415f519d").unwrap(); assert_eq!(hash, expected); } @@ -473,7 +473,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_v2); - let expected = Hash256::from_str("h:e851362bab643dc066b9d3c22c0fa0d67bc7b0cb520c689765e2292f4e7f435e").unwrap(); + let expected = Hash256::from_str("e851362bab643dc066b9d3c22c0fa0d67bc7b0cb520c689765e2292f4e7f435e").unwrap(); assert_eq!(hash, expected); } @@ -524,19 +524,19 @@ mod test { let state_element = StateElement { leaf_index: 1, merkle_proof: vec![ - Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ], }; let file_contract_element_v2 = V2FileContractElement { - id: Hash256::from_str("h:0707070000000000000000000000000000000000000000000000000000000000").unwrap().into(), + id: Hash256::from_str("0707070000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, v2_file_contract: file_contract_v2, }; let hash = Encoder::encode_and_hash(&file_contract_element_v2); - let expected = Hash256::from_str("h:3005594b14c1615aadaef2d8558713ebeabfa7d54f1dec671ba67ea8264816e6").unwrap(); + let expected = Hash256::from_str("3005594b14c1615aadaef2d8558713ebeabfa7d54f1dec671ba67ea8264816e6").unwrap(); assert_eq!(hash, expected); } @@ -587,13 +587,13 @@ mod test { let state_element = StateElement { leaf_index: 1, merkle_proof: vec![ - Hash256::from_str("h:0405060000000000000000000000000000000000000000000000000000000000").unwrap(), - Hash256::from_str("h:0708090000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0405060000000000000000000000000000000000000000000000000000000000").unwrap(), + Hash256::from_str("0708090000000000000000000000000000000000000000000000000000000000").unwrap(), ], }; let file_contract_element_v2 = V2FileContractElement { - id: Hash256::from_str("h:0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), + id: Hash256::from_str("0102030000000000000000000000000000000000000000000000000000000000").unwrap().into(), state_element, v2_file_contract: file_contract_v2.clone(), }; @@ -604,7 +604,7 @@ mod test { }; let hash = Encoder::encode_and_hash(&file_contract_revision_v2); - let expected = Hash256::from_str("h:4f23582ec40570345f72adab8cd6249c0167669b78aec9ac7209befefc281f4f").unwrap(); + let expected = Hash256::from_str("4f23582ec40570345f72adab8cd6249c0167669b78aec9ac7209befefc281f4f").unwrap(); assert_eq!(hash, expected); } @@ -614,16 +614,16 @@ mod test { "siacoinInputs": [ { "parent": { - "id": "h:b49cba94064a92a75bf8c6f9d32ab18f38bfb14a2252e3e117d04da89d536f29", + "id": "b49cba94064a92a75bf8c6f9d32ab18f38bfb14a2252e3e117d04da89d536f29", "stateElement": { "leafIndex": 302, "merkleProof": [ - "h:6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", - "h:eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", - "h:04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", - "h:16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", - "h:1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", - "h:31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" + "6f41d366712e9dfa423160b5388f3faf673addf43566d7b3562106d15b833f46", + "eb7df5e13eccd812a47f29a233bbf3212b7379ca6dd20ba9981524bfd5eadce6", + "04104cbada51333f8f37a6eb71f1e8cb287da2d62469568a8a36dc8c76602c80", + "16aac5c671d49d8cfc5493cb4c6f34889e30a0d283745c6473406bd60ab5e754", + "1b9ccf2b6f555687b1384091faa9ed1c154f41aaff81dcf393295383ca99f518", + "31337c9db5cdd181f5ff142bd490f779eedb1485e5dd905743280aeac3cd7ac9" ], }, "siacoinOutput": { @@ -665,7 +665,7 @@ mod test { let tx = serde_json::from_value::(j).unwrap(); let hash = tx.input_sig_hash(); - let expected = Hash256::from_str("h:ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); + let expected = Hash256::from_str("ef2f59bb25300bed9accbdcd95e1a2bd9f146ab6b474002670dc908ad68aacac").unwrap(); assert_eq!(hash, expected); } @@ -675,18 +675,18 @@ mod test { "siacoinInputs": [ { "parent": { - "id": "h:f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", + "id": "f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", "stateElement": { "leafIndex": 3, "merkleProof": [ - "h:ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "h:467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "h:64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "h:31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "h:12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "h:eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "h:e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "h:e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + "ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" ] }, "siacoinOutput": { @@ -738,11 +738,11 @@ mod test { } fn test_siacoin_output_id_new() { - let txid = Hash256::from_str("h:31be0badc64d40fbcb91b63835c07d75ab49addd1fc1d839b8415e1e5ff38cb5").unwrap(); + let txid = Hash256::from_str("31be0badc64d40fbcb91b63835c07d75ab49addd1fc1d839b8415e1e5ff38cb5").unwrap(); let output_index = 0u32; let output_id = SiacoinOutputId::new(txid, output_index); let expected = SiacoinOutputId( - Hash256::from_str("h:47b2ceee0a9e246d5f997129a250ecb3d0917f5e844989d520e246145349d292").unwrap(), + Hash256::from_str("47b2ceee0a9e246d5f997129a250ecb3d0917f5e844989d520e246145349d292").unwrap(), ); assert_eq!(output_id, expected); } diff --git a/src/transport/client/native.rs b/src/transport/client/native.rs index f894568..c9719fb 100644 --- a/src/transport/client/native.rs +++ b/src/transport/client/native.rs @@ -164,7 +164,7 @@ mod tests { async fn test_api_events() { use crate::types::Hash256; let request = GetEventRequest { - txid: Hash256::from_str("h:77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), + txid: Hash256::from_str("77c5ae2220eac76dd841e365bb14fcba5499977e6483472b96f4a83bcdd6c892").unwrap(), }; let _response = test_dispatch(request).await; } diff --git a/src/transport/client/wasm.rs b/src/transport/client/wasm.rs index 9ecfea2..b007a6b 100644 --- a/src/transport/client/wasm.rs +++ b/src/transport/client/wasm.rs @@ -139,16 +139,16 @@ mod wasm_tests { "siacoinInputs": [ { "parent": { - "id": "h:27248ab562cbbee260e07ccae87c74aae71c9358d7f91eee25837e2011ce36d3", + "id": "27248ab562cbbee260e07ccae87c74aae71c9358d7f91eee25837e2011ce36d3", "leafIndex": 21867, "merkleProof": [ - "h:ac2fdcbed40f103e54b0b1a37c20a865f6f1f765950bc6ac358ff3a0e769da50", - "h:b25570eb5c106619d4eef5ad62482023df7a1c7461e9559248cb82659ebab069", - "h:baa78ec23a169d4e9d7f801e5cf25926bf8c29e939e0e94ba065b43941eb0af8", - "h:239857343f2997462bed6c253806cf578d252dbbfd5b662c203e5f75d897886d", - "h:ad727ef2112dc738a72644703177f730c634a0a00e0b405bd240b0da6cdfbc1c", - "h:4cfe0579eabafa25e98d83c3b5d07ae3835ce3ea176072064ea2b3be689e99aa", - "h:736af73aa1338f3bc28d1d8d3cf4f4d0393f15c3b005670f762709b6231951fc" + "ac2fdcbed40f103e54b0b1a37c20a865f6f1f765950bc6ac358ff3a0e769da50", + "b25570eb5c106619d4eef5ad62482023df7a1c7461e9559248cb82659ebab069", + "baa78ec23a169d4e9d7f801e5cf25926bf8c29e939e0e94ba065b43941eb0af8", + "239857343f2997462bed6c253806cf578d252dbbfd5b662c203e5f75d897886d", + "ad727ef2112dc738a72644703177f730c634a0a00e0b405bd240b0da6cdfbc1c", + "4cfe0579eabafa25e98d83c3b5d07ae3835ce3ea176072064ea2b3be689e99aa", + "736af73aa1338f3bc28d1d8d3cf4f4d0393f15c3b005670f762709b6231951fc" ], "siacoinOutput": { "value": "772999980000000000000000000", From 68cc1df297aa34c36d9a55b9e041d6b245368a42 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:24:52 -0500 Subject: [PATCH 207/233] refactor Address serde to remove addr: prefix --- src/types.rs | 83 +++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/src/types.rs b/src/types.rs index b04a8ea..4e21575 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,13 +2,14 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; use blake2b_simd::Params; use chrono::{DateTime, Utc}; -use hex::FromHexError; +use hex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use std::convert::From; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::fmt; use std::str::FromStr; +use thiserror::Error; mod hash; pub use hash::{Hash256, Hash256Error}; @@ -60,7 +61,7 @@ impl<'de> Deserialize<'de> for Address { type Value = Address; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'addr:' and followed by a 76-character hex string") + formatter.write_str("a 76-character hex string representing a Sia address") } fn visit_str(self, value: &str) -> Result @@ -76,12 +77,6 @@ impl<'de> Deserialize<'de> for Address { } impl Address { - pub fn str_without_prefix(&self) -> String { - let bytes = self.0 .0.as_ref(); - let checksum = blake2b_checksum(bytes); - format!("{}{}", hex::encode(bytes), hex::encode(checksum)) - } - pub fn standard_address_v1(pubkey: &PublicKey) -> Self { let hash = standard_unlock_hash(pubkey); Address(hash) @@ -95,60 +90,50 @@ impl Encodable for Address { } impl fmt::Display for Address { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "addr:{}", self.str_without_prefix()) } -} - -impl fmt::Display for ParseAddressError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Failed to parse Address: {:?}", self) } -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum ParseAddressError { - #[serde(rename = "Address must begin with addr: prefix")] - MissingPrefix, - InvalidHexEncoding(String), - InvalidChecksum, - InvalidLength, + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bytes = self.0 .0.as_ref(); + let checksum = blake2b_checksum(bytes); + write!(f, "{}{}", hex::encode(bytes), hex::encode(checksum)) + } } -impl From for ParseAddressError { - fn from(e: FromHexError) -> Self { ParseAddressError::InvalidHexEncoding(e.to_string()) } +#[derive(Debug, Error)] +pub enum AddressError { + #[error("Address::from_str Failed to decode hex: {0}")] + InvalidHex(#[from] hex::FromHexError), + #[error("Address::from_str: Invalid length, expected 38 byte hex string, found: {0}")] + InvalidLength(String), + #[error("Address::from_str: invalid checksum, expected:{expected}, found:{found}")] + InvalidChecksum { expected: String, found: String }, } impl FromStr for Address { - type Err = ParseAddressError; + type Err = AddressError; fn from_str(s: &str) -> Result { - if !s.starts_with("addr:") { - return Err(ParseAddressError::MissingPrefix); - } - - let without_prefix = &s[ADDRESS_CHECKSUM_LENGTH - 1..]; - if without_prefix.len() != (ADDRESS_HASH_LENGTH + ADDRESS_CHECKSUM_LENGTH) * 2 { - return Err(ParseAddressError::InvalidLength); + // An address consists of a 32 byte blake2h hash followed by a 6 byte checksum + let address_bytes = hex::decode(s)?; + if address_bytes.len() != ADDRESS_HASH_LENGTH + ADDRESS_CHECKSUM_LENGTH { + return Err(AddressError::InvalidLength(s.to_owned())); } - let (address_hex, checksum_hex) = without_prefix.split_at(ADDRESS_HASH_LENGTH * 2); - - let address_bytes: [u8; ADDRESS_HASH_LENGTH] = hex::decode(address_hex) - .map_err(ParseAddressError::from)? - .try_into() - .map_err(|_| ParseAddressError::InvalidLength)?; + let hash_bytes = &address_bytes[0..ADDRESS_HASH_LENGTH]; + let checksum_bytes = &address_bytes[ADDRESS_HASH_LENGTH..]; - let checksum = hex::decode(checksum_hex).map_err(ParseAddressError::from)?; - let checksum_bytes: [u8; ADDRESS_CHECKSUM_LENGTH] = - checksum.try_into().map_err(|_| ParseAddressError::InvalidLength)?; - - if checksum_bytes != blake2b_checksum(&address_bytes) { - return Err(ParseAddressError::InvalidChecksum); + let checksum = blake2b_checksum(hash_bytes); + if checksum_bytes != checksum { + return Err(AddressError::InvalidChecksum { + expected: hex::encode(checksum), + found: hex::encode(checksum_bytes), + }); } - - Ok(Address(Hash256(address_bytes))) + let inner_hash = Hash256::try_from(hash_bytes).expect("hash_bytes is 32 bytes long"); + Ok(Address(inner_hash)) } } -// Sia uses the first 6 bytes of blake2b(preimage) appended -// to address as checksum +/// Return the first 6 bytes of the blake2b(preimage) hash +/// Used in generating the checksum for a Sia address fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { let hash = Params::new().hash_length(32).to_state().update(preimage).finalize(); hash.as_bytes()[0..6].try_into().expect("array is 64 bytes long") From 33146a464056b658a1dcd275aecbdeac24ac4e39 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:28:01 -0500 Subject: [PATCH 208/233] remove addr: prefix from all Address instances in unit tests --- src/tests/encoding.rs | 14 +++---- src/tests/serde.rs | 69 ++++++++++++++++++---------------- src/tests/spend_policy.rs | 6 +-- src/tests/transaction.rs | 18 ++++----- src/transport/client/native.rs | 6 +-- src/transport/client/wasm.rs | 6 +-- 6 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/tests/encoding.rs b/src/tests/encoding.rs index 1f2f090..9860ca1 100644 --- a/src/tests/encoding.rs +++ b/src/tests/encoding.rs @@ -47,7 +47,7 @@ mod test { let address = policy.address(); let expected = - Address::from_str("addr:188b997bb99dee13e95f92c3ea150bd76b3ec72e5ba57b0d57439a1a6e2865e9b25ea5d1825e").unwrap(); + Address::from_str("188b997bb99dee13e95f92c3ea150bd76b3ec72e5ba57b0d57439a1a6e2865e9b25ea5d1825e").unwrap(); assert_eq!(address, expected); } @@ -59,7 +59,7 @@ mod test { let address = policy.address(); let expected = - Address::from_str("addr:60c74e0ce5cede0f13f83b0132cb195c995bc7688c9fac34bbf2b14e14394b8bbe2991bc017f").unwrap(); + Address::from_str("60c74e0ce5cede0f13f83b0132cb195c995bc7688c9fac34bbf2b14e14394b8bbe2991bc017f").unwrap(); assert_eq!(address, expected); } @@ -76,7 +76,7 @@ mod test { let address = policy.address(); let expected = - Address::from_str("addr:55a7793237722c6df8222fd512063cb74228085ef1805c5184713648c159b919ac792fbad0e1").unwrap(); + Address::from_str("55a7793237722c6df8222fd512063cb74228085ef1805c5184713648c159b919ac792fbad0e1").unwrap(); assert_eq!(address, expected); } @@ -90,7 +90,7 @@ mod test { let address = policy.address(); let expected = - Address::from_str("addr:a4d5a06d8d3c2e45aa26627858ce8e881505ae3c9d122a1d282c7824163751936cffb347e435").unwrap(); + Address::from_str("a4d5a06d8d3c2e45aa26627858ce8e881505ae3c9d122a1d282c7824163751936cffb347e435").unwrap(); assert_eq!(address, expected); } @@ -106,7 +106,7 @@ mod test { let address = policy.address(); let expected = - Address::from_str("addr:4179b53aba165e46e4c85b3c8766bb758fb6f0bfa5721550b81981a3ec38efc460557dc1ded4").unwrap(); + Address::from_str("4179b53aba165e46e4c85b3c8766bb758fb6f0bfa5721550b81981a3ec38efc460557dc1ded4").unwrap(); assert_eq!(address, expected); } @@ -120,7 +120,7 @@ mod test { let sub_policy = SpendPolicy::UnlockConditions(unlock_condition); let base_address = sub_policy.address(); let expected = - Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a").unwrap(); + Address::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a").unwrap(); assert_eq!(base_address, expected); let policy = SpendPolicy::Threshold { @@ -129,7 +129,7 @@ mod test { }; let address = policy.address(); let expected = - Address::from_str("addr:1498a58c843ce66740e52421632d67a0f6991ea96db1fc97c29e46f89ae56e3534078876331d").unwrap(); + Address::from_str("1498a58c843ce66740e52421632d67a0f6991ea96db1fc97c29e46f89ae56e3534078876331d").unwrap(); assert_eq!(address, expected); } diff --git a/src/tests/serde.rs b/src/tests/serde.rs index bc7171e..6b4c76b 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -44,11 +44,11 @@ fn test_serde_event_v2_contract_resolution_finalization() { "expirationHeight": 20, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:c899f7795bb20c94e57c764f06699e09e6ad071ad95539eef4fb505e79ab22e8be4d64067ccc" + "address": "c899f7795bb20c94e57c764f06699e09e6ad071ad95539eef4fb505e79ab22e8be4d64067ccc" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -67,11 +67,11 @@ fn test_serde_event_v2_contract_resolution_finalization() { "expirationHeight": 20, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:c899f7795bb20c94e57c764f06699e09e6ad071ad95539eef4fb505e79ab22e8be4d64067ccc" + "address": "c899f7795bb20c94e57c764f06699e09e6ad071ad95539eef4fb505e79ab22e8be4d64067ccc" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -108,7 +108,7 @@ mod test { fn test_serde_address() { test_serde!( Address, - json!("addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") + json!("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") ); } @@ -129,7 +129,7 @@ mod test { fn test_serde_siacoin_output() { let j = json!({ "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + "address": "591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" }); test_serde!(SiacoinOutput, j); } @@ -145,14 +145,19 @@ mod test { fn test_serde_siacoin_element() { let j = json!( { - "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": ["8dfc4731c4ef4bf35f789893e72402a39c7ea63ba9e75565cb11000d0159959e"], + "id": "0102030000000000000000000000000000000000000000000000000000000000", + "stateElement": { + "leafIndex": 1, + "merkleProof": [ + "0405060000000000000000000000000000000000000000000000000000000000", + "0708090000000000000000000000000000000000000000000000000000000000" + ] + }, "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + "value": "1", + "address": "72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a" }, - "maturityHeight": 154 + "maturityHeight": 0 } ); serde_json::from_value::(j).unwrap(); @@ -165,7 +170,7 @@ mod test { "merkleProof": null, "siacoinOutput": { "value": "300000000000000000000000000000", - "address": "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + "address": "591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" }, "maturityHeight": 154 } @@ -203,11 +208,11 @@ mod test { "expirationHeight": 210, "renterOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "hostOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "missedHostValue": "0", "totalCollateral": "0", @@ -239,7 +244,7 @@ mod test { "merkleProof": null, "siacoinOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 345 }, @@ -282,11 +287,11 @@ mod test { "expirationHeight": 221, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -306,11 +311,11 @@ mod test { "expirationHeight": 221, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -327,11 +332,11 @@ mod test { "expirationHeight": 231, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -353,7 +358,7 @@ mod test { "merkleProof": null, "siacoinOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 347 }, @@ -400,11 +405,11 @@ mod test { "expirationHeight": 189, "renterOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { "value": "0", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -424,7 +429,7 @@ mod test { "merkleProof": null, "siacoinOutput": { "value": "10000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 334 }, @@ -460,7 +465,7 @@ mod test { ], "siacoinOutput": { "value": "256394172736732570239334030000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 0 }, @@ -484,11 +489,11 @@ mod test { "siacoinOutputs": [ { "value": "10400000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, { "value": "245994172736732570239334030000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } ], "minerFee": "0" @@ -518,7 +523,7 @@ mod test { ], "siacoinOutput": { "value": "300000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 145 }, @@ -542,11 +547,11 @@ mod test { "siacoinOutputs": [ { "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, { "value": "299000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } ], "minerFee": "0" diff --git a/src/tests/spend_policy.rs b/src/tests/spend_policy.rs index 3c5b129..452d05c 100644 --- a/src/tests/spend_policy.rs +++ b/src/tests/spend_policy.rs @@ -67,11 +67,11 @@ mod test { let j = json!( { "type": "opaque", - "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" + "policy": "f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" } ); let address = - Address::from_str("addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a").unwrap(); + Address::from_str("f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a").unwrap(); let spend_policy_deser: SpendPolicy = serde_json::from_value::(j).unwrap().into(); let spend_policy = SpendPolicy::Opaque(address); @@ -115,7 +115,7 @@ mod test { }, { "type": "opaque", - "policy": "addr:f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" + "policy": "f72e84ee9e344e424a6764068ffd7fdce4b4e50609892c6801bc1ead79d3ae0d71791b277a3a" } ] } diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 94488de..1d2017e 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -62,7 +62,7 @@ mod test { fn test_siacoin_output_encode_v1() { let vout = SiacoinOutput { value: 1u64.into(), - address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") + address: Address::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") .unwrap(), }; @@ -74,7 +74,7 @@ mod test { fn test_siacoin_output_encode_v2() { let vout = SiacoinOutput { value: 1u64.into(), - address: Address::from_str("addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") + address: Address::from_str("72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a") .unwrap(), }; @@ -97,7 +97,7 @@ mod test { siacoin_output: SiacoinOutput { value: 1u64.into(), address: Address::from_str( - "addr:72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a", + "72b0762b382d4c251af5ae25b6777d908726d75962e5224f98d7f619bb39515dd64b9a56043a", ) .unwrap(), }, @@ -628,7 +628,7 @@ mod test { }, "siacoinOutput": { "value": "288594172736732570239334030000", - "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" + "address": "2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" }, "maturityHeight": 0 }, @@ -652,11 +652,11 @@ mod test { "siacoinOutputs": [ { "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, { "value": "287594172736732570239334030000", - "address": "addr:2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" + "address": "2757c80b7ec2e493a138fed45b906f9f5735a992b68dcbd2069fbdf418c8b25158f3ac7a816b" } ], "minerFee": "0" @@ -691,7 +691,7 @@ mod test { }, "siacoinOutput": { "value": "300000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 145 }, @@ -715,11 +715,11 @@ mod test { "siacoinOutputs": [ { "value": "1000000000000000000000000000", - "address": "addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, { "value": "299000000000000000000000000000", - "address": "addr:f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } ], "minerFee": "0" diff --git a/src/transport/client/native.rs b/src/transport/client/native.rs index c9719fb..6e38e9c 100644 --- a/src/transport/client/native.rs +++ b/src/transport/client/native.rs @@ -152,10 +152,8 @@ mod tests { #[tokio::test] async fn test_api_address_balance() { let request = AddressBalanceRequest { - address: Address::from_str( - "addr:591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f", - ) - .unwrap(), + address: Address::from_str("591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f") + .unwrap(), }; let _response = test_dispatch(request).await; } diff --git a/src/transport/client/wasm.rs b/src/transport/client/wasm.rs index b007a6b..f67de01 100644 --- a/src/transport/client/wasm.rs +++ b/src/transport/client/wasm.rs @@ -152,7 +152,7 @@ mod wasm_tests { ], "siacoinOutput": { "value": "772999980000000000000000000", - "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" + "address": "1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" }, "maturityHeight": 0 }, @@ -170,11 +170,11 @@ mod wasm_tests { "siacoinOutputs": [ { "value": "2000000000000000000000000", - "address": "addr:1d9a926b1e14b54242375c7899a60de883c8cad0a45a49a7ca2fdb6eb52f0f01dfe678918204" + "address": "1d9a926b1e14b54242375c7899a60de883c8cad0a45a49a7ca2fdb6eb52f0f01dfe678918204" }, { "value": "770999970000000000000000000", - "address": "addr:1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" + "address": "1599ea80d9af168ce823e58448fad305eac2faf260f7f0b56481c5ef18f0961057bf17030fb3" } ], "minerFee": "10000000000000000000" From 862adf2f889f7752e45848ba2146e96db5be2f54 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:39:05 -0500 Subject: [PATCH 209/233] fix merkle_proof field in StateElement serde - walletd is capable of providing a JSON that does not include this field, includes it as `null` or includes it as empty array --- src/types/transaction.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 320073a..e21532e 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1,6 +1,7 @@ use crate::encoding::{Encodable, Encoder}; use crate::types::{Address, ChainIndex, Hash256, Keypair, PublicKey, Signature, SpendPolicy, UnlockCondition, UnlockKey}; +use crate::utils::deserialize_null_as_empty_vec; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use derive_more::{Add, AddAssign, Deref, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign, Sum}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -271,7 +272,7 @@ impl Encodable for SatisfiedPolicy { #[serde(rename_all = "camelCase")] pub struct StateElement { pub leaf_index: u64, - #[serde(default)] + #[serde(deserialize_with = "deserialize_null_as_empty_vec", default)] pub merkle_proof: Vec, } From c4027f70ae46fe33781002f05fe3464389b8f255 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:39:37 -0500 Subject: [PATCH 210/233] add more explitcit unit test for StateElement serde --- src/tests/serde.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 6b4c76b..471c4a9 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -134,13 +134,30 @@ mod test { test_serde!(SiacoinOutput, j); } + // check that merkleProof field serde is the same when it is null, missing or empty fn test_serde_state_element() { let j = json!({ "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", "leafIndex": 21, "merkleProof": null }); - serde_json::from_value::(j).unwrap(); + let null_proof = serde_json::from_value::(j).unwrap(); + + let j = json!({ + "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21, + "merkleProof": [] + }); + let empty_proof = serde_json::from_value::(j).unwrap(); + + let j = json!({ + "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", + "leafIndex": 21 + }); + let missing_proof = serde_json::from_value::(j).unwrap(); + + assert_eq!(null_proof, empty_proof); + assert_eq!(null_proof, missing_proof); } fn test_serde_siacoin_element() { From aefef42b036a9b25d039e57164b986571df56550 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:46:13 -0500 Subject: [PATCH 211/233] update serde test to account for prefix removals and state element changes --- src/tests/serde.rs | 204 +++++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 471c4a9..20d2dd6 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -276,117 +276,119 @@ mod test { } fn test_serde_event_v2_contract_resolution_renewal() { - let j = json!( + let json_str = r#" { - "id": "debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", + "id": "5d565129957e1493902123f6d58775593a53ccbff1e30342defaf563853c30b4", "index": { - "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + "height": 203, + "id": "f5674e39f155f1d5afe6cd2315a8b6c89843c1fbc19b13d8c6b3636b20cb537c" }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 347, + "confirmations": 4, "type": "v2ContractResolution", "data": { - "resolution": { - "parent": { - "id": "06b6349f4e76819aa36b7f1190d276b9ca97f0d5fc4564f153d6a36ed3c38033", - "leafIndex": 423, - "merkleProof": [ - "ba1427aad85e9985b61f262a2ea768a74f24af02d7e6c17f0cdb92559e7951ea", - "147817a1d32c3f068be5456d935bc6cddd6306fe5633b576d91260d43a82e6d8", - "f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c", - "1cdf40c0a759931ff590496b953938fbe7315394ce3726b4e4c4b81fed3d5498" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03", - "hostSignature": "sig:7d6f0e5b799c689dca7b55b1ff8ad028c7285b777d6df0e68235bde5778802adfb87e80afaf5d6c9b9fa63cd0e433aaa7189e3fdf2c7bf374c0ca20858071f03" - } + "resolution": { + "parent": { + "id": "d219a1300698e798338df61f6f816f593672f71bce274d5130e1ba95e1d63814", + "stateElement": { + "leafIndex": 423 + }, + "v2FileContract": { + "capacity": 0, + "filesize": 0, + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "type": "renewal", - "resolution": { - "finalRevision": { - "filesize": 0, - "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 211, - "expirationHeight": 221, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 18446744073709551615u64, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "newContract": { - "filesize": 0, - "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 221, - "expirationHeight": 231, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "hostSignature": "sig:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "renterRollover": "0", - "hostRollover": "0", - "renterSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08", - "hostSignature": "sig:54a4bb0247518f62b20bf141686e2c05858e91acd23ae5e42436d173e331aca92af344e8cb9b5da98f0bdef01c7b7d840cbe7e781b8f7acc7c33b0fa44c7ef08" - } + "hostOutput": { + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "3aaf47eb60d992bced4818291eb1b7773e20a731df48857474715602db31a12fddf29170337803f6dd1ce95e1e2043714c2b3bcb99925ea37ad2cf4880922c02", + "hostSignature": "3aaf47eb60d992bced4818291eb1b7773e20a731df48857474715602db31a12fddf29170337803f6dd1ce95e1e2043714c2b3bcb99925ea37ad2cf4880922c02" + } }, - "siacoinElement": { - "id": "debd3b8461d1aaa9011ba62d79c7ed7991eb0c60f9576880faadf2a8051aad54", - "leafIndex": 427, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "type": "renewal", + "resolution": { + "finalRevision": { + "capacity": 0, + "filesize": 0, + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 211, + "expirationHeight": 221, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, - "maturityHeight": 347 + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 18446744073709551615, + "renterSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "newContract": { + "capacity": 0, + "filesize": 0, + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 221, + "expirationHeight": 231, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + }, + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "hostSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "renterRollover": "0", + "hostRollover": "0", + "renterSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f", + "hostSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f" + } + }, + "siacoinElement": { + "id": "5d565129957e1493902123f6d58775593a53ccbff1e30342defaf563853c30b4", + "stateElement": { + "leafIndex": 427 }, - "missed": false - } - } - ); - - let _event = serde_json::from_value::(j).unwrap(); + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 347 + }, + "missed": false + }, + "maturityHeight": 347, + "timestamp": "2024-11-15T19:41:06Z", + "relevant": [ + "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + ] + } + "#; - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde + let _event = serde_json::from_str::(json_str).unwrap(); } #[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) From a9bce542352ca8d1d7aa2ea12d328debdb514bf2 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:46:53 -0500 Subject: [PATCH 212/233] rename symbol BlockID -> BlockId --- src/transport/endpoints.rs | 4 ++-- src/types.rs | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 469355e..019b7de 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -1,5 +1,5 @@ use crate::transport::client::{ApiClientError, Body, EndpointSchema, EndpointSchemaBuilder, SchemaMethod}; -use crate::types::{Address, ApiApplyUpdate, BlockID, ChainIndex, Currency, Event, Hash256, SiacoinElement, +use crate::types::{Address, ApiApplyUpdate, BlockId, ChainIndex, Currency, Event, Hash256, SiacoinElement, V1Transaction, V2Transaction}; use crate::utils::deserialize_null_as_empty_vec; use chrono::{DateTime, Utc}; @@ -154,7 +154,7 @@ pub struct ConsensusTipstateResponse { #[derive(Clone, Deserialize, Serialize, Debug)] pub struct ConsensusUpdatesRequest { pub height: u64, - pub block_hash: BlockID, + pub block_hash: BlockId, pub limit: Option, } diff --git a/src/types.rs b/src/types.rs index 4e21575..ff1a582 100644 --- a/src/types.rs +++ b/src/types.rs @@ -140,17 +140,17 @@ fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { } #[derive(Clone, Debug, PartialEq)] -pub struct BlockID(pub Hash256); +pub struct BlockId(pub Hash256); -impl From for Hash256 { - fn from(sia_hash: BlockID) -> Self { sia_hash.0 } +impl From for Hash256 { + fn from(sia_hash: BlockId) -> Self { sia_hash.0 } } -impl From for BlockID { - fn from(h256: Hash256) -> Self { BlockID(h256) } +impl From for BlockId { + fn from(h256: Hash256) -> Self { BlockId(h256) } } -impl<'de> Deserialize<'de> for BlockID { +impl<'de> Deserialize<'de> for BlockId { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -158,7 +158,7 @@ impl<'de> Deserialize<'de> for BlockID { struct BlockIDVisitor; impl<'de> serde::de::Visitor<'de> for BlockIDVisitor { - type Value = BlockID; + type Value = BlockId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string prefixed with 'bid:' and followed by a 64-character hex string") @@ -170,7 +170,7 @@ impl<'de> Deserialize<'de> for BlockID { { if let Some(hex_str) = value.strip_prefix("bid:") { Hash256::from_str(hex_str) - .map(BlockID) + .map(BlockId) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) @@ -182,7 +182,7 @@ impl<'de> Deserialize<'de> for BlockID { } } -impl Serialize for BlockID { +impl Serialize for BlockId { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -191,14 +191,14 @@ impl Serialize for BlockID { } } -impl fmt::Display for BlockID { +impl fmt::Display for BlockId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct ChainIndex { pub height: u64, - pub id: BlockID, + pub id: BlockId, } // TODO unit test From 61a12b32b35be1f9a6437bb4cf3787754b22a4b4 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:48:00 -0500 Subject: [PATCH 213/233] derive From for Hash256 and vice versa instead of manually implementings --- src/types.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/types.rs b/src/types.rs index ff1a582..d39d945 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,6 +2,7 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; use blake2b_simd::Params; use chrono::{DateTime, Utc}; +use derive_more::{From, Into}; use hex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; @@ -139,17 +140,9 @@ fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { hash.as_bytes()[0..6].try_into().expect("array is 64 bytes long") } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, From, Into)] pub struct BlockId(pub Hash256); -impl From for Hash256 { - fn from(sia_hash: BlockId) -> Self { sia_hash.0 } -} - -impl From for BlockId { - fn from(h256: Hash256) -> Self { BlockId(h256) } -} - impl<'de> Deserialize<'de> for BlockId { fn deserialize(deserializer: D) -> Result where From 2a87e2588e02986a1c81bc20dee958ce0e6828fa Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:49:04 -0500 Subject: [PATCH 214/233] remove unused import --- src/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index d39d945..8f57e58 100644 --- a/src/types.rs +++ b/src/types.rs @@ -6,7 +6,6 @@ use derive_more::{From, Into}; use hex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; -use std::convert::From; use std::convert::{TryFrom, TryInto}; use std::fmt; use std::str::FromStr; From 26e634fcfc7efb3e2bbd8105121eb900d0dab041 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:50:41 -0500 Subject: [PATCH 215/233] remove bid: prefix from BlockId serde derive Display instead of manually implementing for BlockId --- src/types.rs | 50 +++----------------------------------------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/src/types.rs b/src/types.rs index 8f57e58..ddcd966 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,7 @@ use crate::blake2b_internal::standard_unlock_hash; use crate::encoding::{Encodable, Encoder}; use blake2b_simd::Params; use chrono::{DateTime, Utc}; -use derive_more::{From, Into}; +use derive_more::{Display, From, Into}; use hex; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; @@ -139,54 +139,10 @@ fn blake2b_checksum(preimage: &[u8]) -> [u8; 6] { hash.as_bytes()[0..6].try_into().expect("array is 64 bytes long") } -#[derive(Clone, Debug, PartialEq, From, Into)] +#[derive(Clone, Debug, Display, PartialEq, From, Into, Serialize, Deserialize)] +#[serde(transparent)] pub struct BlockId(pub Hash256); -impl<'de> Deserialize<'de> for BlockId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct BlockIDVisitor; - - impl<'de> serde::de::Visitor<'de> for BlockIDVisitor { - type Value = BlockId; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'bid:' and followed by a 64-character hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Some(hex_str) = value.strip_prefix("bid:") { - Hash256::from_str(hex_str) - .map(BlockId) - .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(BlockIDVisitor) - } -} - -impl Serialize for BlockId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl fmt::Display for BlockId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } -} - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct ChainIndex { pub height: u64, From c75b0e03b384fb5efd397d6024c5518da3cf32a4 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 14:55:12 -0500 Subject: [PATCH 216/233] update SiacoinElement unit test to account for type changes --- src/tests/serde.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 20d2dd6..241e7b2 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -180,19 +180,20 @@ mod test { serde_json::from_value::(j).unwrap(); } - fn test_serde_siacoin_element_null_merkle_proof() { - let j = json!( { - "id": "dc07e5bf84fbda867a7ed7ca80c6d1d81db05cef16ff38f6ba80b6bf01e1ddb1", - "leafIndex": 21, - "merkleProof": null, + fn test_serde_siacoin_element_missing_merkle_proof() { + let json_str = r#" + { + "id": "16406893374eb18eeea95e8c0d6b6c325275ecb99cf2fec7a6708b0b8def75bd", + "stateElement": { + "leafIndex": 391 + }, "siacoinOutput": { - "value": "300000000000000000000000000000", - "address": "591fcf237f8854b5653d1ac84ae4c107b37f148c3c7b413f292d48db0c25a8840be0653e411f" + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "maturityHeight": 154 - } - ); - serde_json::from_value::(j).unwrap(); + "maturityHeight": 334 + }"#; + serde_json::from_str::(json_str).unwrap(); } fn test_serde_event_v2_contract_resolution_storage_proof() { From d68afb82ecc7fb2a33eb6839ae921d5640b8b57a Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 16:43:29 -0500 Subject: [PATCH 217/233] refactor Signature serde to remove sig: prefix refactor SignatureError, PublicKeyError, KeypairError to match standard error handling pattern remove PrivateKeyError --- src/types.rs | 2 +- src/types/keypair.rs | 56 +++++----- src/types/signature.rs | 223 +++++++++++++-------------------------- src/types/transaction.rs | 12 +-- 4 files changed, 109 insertions(+), 184 deletions(-) diff --git a/src/types.rs b/src/types.rs index ddcd966..ef68785 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,7 +18,7 @@ mod signature; pub use signature::{Signature, SignatureError}; mod keypair; -pub use keypair::{Keypair, PrivateKeyError, PublicKey, PublicKeyError}; +pub use keypair::{Keypair, KeypairError, PublicKey, PublicKeyError}; mod spend_policy; pub use spend_policy::*; diff --git a/src/types/keypair.rs b/src/types/keypair.rs index 5d2c7bc..fd884a7 100644 --- a/src/types/keypair.rs +++ b/src/types/keypair.rs @@ -6,24 +6,7 @@ use std::fmt; use std::str::FromStr; use thiserror::Error; -use crate::types::{Address, Signature, SignatureError, SpendPolicy}; - -#[derive(Debug, Error)] -pub enum PublicKeyError { - #[error("invalid public key length: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] - InvalidLength(String), - #[error("public key invalid hex: expected 32 byte hex string, found {0}")] - InvalidHex(String), - #[error("public key invalid: corrupt curve point {0}")] - CorruptPoint(String), - #[error("public key invalid: from_bytes failed {0}")] - ParseBytes(Ed25519SignatureError), -} -#[derive(Debug, Error)] -pub enum PrivateKeyError { - #[error("invalid private key: from_bytes failed {0}")] - InvalidPrivateKey(Ed25519SignatureError), -} +use crate::types::{Address, Signature, SpendPolicy}; /// A Sia Public-Private Keypair /// The purpose of this wrapper type is to limit the functionality of underlying ed25519 types. @@ -35,17 +18,25 @@ pub struct Keypair { private: PrivateKey, } +#[derive(Debug, Error)] +pub enum KeypairError { + #[error("Keypair::verify: invalid signature {0}")] + VerifySignature(#[from] PublicKeyError), + #[error("Keypair::from_private_bytes: invalid private key {0}")] + InvalidPrivateKey(#[from] Ed25519SignatureError), +} + impl Signer for Keypair { /// Sign a message with this keypair's secret key. fn try_sign(&self, message: &[u8]) -> Result { let expanded: ExpandedSecretKey = (&self.private.0).into(); - Ok(Signature::new(expanded.sign(message, &self.public.0))) + Ok(Signature::from(expanded.sign(message, &self.public.0))) } } impl Keypair { - pub fn from_private_bytes(bytes: &[u8]) -> Result { - let secret = SecretKey::from_bytes(bytes).map_err(PrivateKeyError::InvalidPrivateKey)?; + pub fn from_private_bytes(bytes: &[u8]) -> Result { + let secret = SecretKey::from_bytes(bytes)?; let public = PublicKey(Ed25519PublicKey::from(&secret)); let private = PrivateKey(secret); Ok(Keypair { public, private }) @@ -54,8 +45,8 @@ impl Keypair { pub fn sign(&self, message: &[u8]) -> Signature { Signer::sign(self, message) } /// Verify a signature of a message with this keypair's public key. - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { - self.public.verify(message, signature) + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), KeypairError> { + Ok(self.public.verify(message, signature)?) } pub fn public(&self) -> PublicKey { self.public.clone() } @@ -68,6 +59,20 @@ struct PrivateKey(SecretKey); #[derive(Clone, Debug, PartialEq)] pub struct PublicKey(pub Ed25519PublicKey); +#[derive(Debug, Error)] +pub enum PublicKeyError { + #[error("invalid public key length: expected 32 byte hex string prefixed with 'ed25519:', found {0}")] + InvalidLength(String), + #[error("public key invalid hex: expected 32 byte hex string, found {0}")] + InvalidHex(String), + #[error("public key invalid: corrupt curve point {0}")] + CorruptPoint(String), + #[error("public key invalid: from_bytes failed {0}")] + ParseBytes(Ed25519SignatureError), + #[error("PublicKey::verify: invalid signature {0}")] + VerifySignature(#[from] Ed25519SignatureError), +} + impl Verifier for PublicKey { /// Verify a signature of a message with this keypair's public key. fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Ed25519SignatureError> { @@ -118,8 +123,9 @@ impl PublicKey { /// Generate the default v2 address from the public key pub fn address(&self) -> Address { SpendPolicy::PublicKey(self.clone()).address() } - pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> { - Verifier::verify(self, message, signature).map_err(SignatureError::VerifyFailed) + /// Verify a signature of a message with this keypair's public key. + pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), PublicKeyError> { + Ok(Verifier::verify(self, message, signature)?) } } diff --git a/src/types/signature.rs b/src/types/signature.rs index 61ea86a..cedc483 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -1,27 +1,56 @@ -use crate::types::keypair::PublicKey; +use crate::types::keypair::{PublicKey, PublicKeyError}; use curve25519_dalek::edwards::CompressedEdwardsY; +use derive_more::{From, Into}; use ed25519_dalek::ed25519::signature::{Error as SignatureCrateError, Signature as SignatureTrait}; -use ed25519_dalek::{Signature as Ed25519Signature, SignatureError as Ed25519SignatureError, SIGNATURE_LENGTH}; +use ed25519_dalek::{Signature as Ed25519Signature, SIGNATURE_LENGTH}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::convert::TryFrom; use std::fmt; use std::str::FromStr; use thiserror::Error; +#[derive(Clone, Debug, PartialEq, From, Into)] +pub struct Signature(pub Ed25519Signature); + #[derive(Debug, Error)] pub enum SignatureError { - #[error("signature parsing failed: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] - Parse(#[from] ed25519_dalek::ed25519::Error), - #[error("invalid signature prefix: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] - InvalidPrefix(String), - #[error("invalid signature, corrupt R point: expected 64 byte hex string ed25519 signature prefixed with 'sig:', found {0}")] - CorruptRPoint(String), - #[error("signature verification failed: {0}")] - VerifyFailed(Ed25519SignatureError), + #[error("Signature::TryFrom<&[u8]>: failed to parse signature from slice {0}")] + ParseSlice(#[from] ed25519_dalek::ed25519::Error), + #[error("Signature::TryFrom<&[u8]>: invalid signature:{0:?}, corrupt R point")] + CorruptRPointSlice(Vec), + #[error("Signature::from_str: invalid signature:{0}, corrupt R point")] + CorruptRPointStr(String), + #[error("Signature::verify: invalid signature: {0}")] + VerifyFailed(#[from] PublicKeyError), } -#[derive(Clone, Debug, PartialEq)] -pub struct Signature(pub Ed25519Signature); +impl Default for Signature { + fn default() -> Self { Signature(Ed25519Signature::try_from([0u8; 64]).expect("00'd signature is valid")) } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:02x}", self.0) } +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Signature::from_str(&s).map_err(serde::de::Error::custom) + } +} // trait bound of Signer for Keypair impl SignatureTrait for Signature { @@ -36,33 +65,29 @@ impl AsRef<[u8]> for Signature { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } -impl Signature { - pub fn new(signature: Ed25519Signature) -> Self { Signature(signature) } +impl TryFrom<&[u8]> for Signature { + type Error = SignatureError; - pub fn from_bytes(bytes: &[u8]) -> Result { + fn try_from(bytes: &[u8]) -> Result { let signature = Ed25519Signature::from_bytes(bytes) .map(Signature) - .map_err(SignatureError::Parse)?; + .map_err(SignatureError::ParseSlice)?; match signature.validate_r_point() { true => Ok(signature), - false => Err(SignatureError::CorruptRPoint(hex::encode(bytes))), + false => Err(SignatureError::CorruptRPointSlice(bytes.to_vec())), } } +} - pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { self.0.to_bytes() } +impl TryFrom> for Signature { + type Error = SignatureError; - // Method for parsing a hex string without the "sig:" prefix - pub fn from_str_no_prefix(hex_str: &str) -> Result { - let signature = Ed25519Signature::from_str(hex_str) - .map(Signature) - .map_err(SignatureError::Parse)?; + fn try_from(bytes: Vec) -> Result { Signature::try_from(bytes.as_slice()) } +} - match signature.validate_r_point() { - true => Ok(signature), - false => Err(SignatureError::CorruptRPoint(hex_str.to_string())), - } - } +impl Signature { + pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { self.0.to_bytes() } /// Check if R value is a valid point on the Ed25519 curve pub fn validate_r_point(&self) -> bool { @@ -73,64 +98,26 @@ impl Signature { } pub fn verify(&self, message: &[u8], public_key: &PublicKey) -> Result<(), SignatureError> { - public_key.verify(message, self) + Ok(public_key.verify(message, self)?) } } -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct SignatureVisitor; - - impl<'de> serde::de::Visitor<'de> for SignatureVisitor { - type Value = Signature; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a 64 byte hex string representing a ed25519 signature prefixed with 'sig:'") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Signature::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - - deserializer.deserialize_str(SignatureVisitor) - } -} - -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{}", self)) - } -} - -impl fmt::Display for Signature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "sig:{:x}", self.0) } -} - -impl fmt::LowerHex for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Delegate to the fmt::LowerHex implementation of the inner Ed25519Signature - fmt::LowerHex::fmt(&self.0, f) - } -} +// impl fmt::LowerHex for Signature { +// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +// // Delegate to the fmt::LowerHex implementation of the inner Ed25519Signature +// fmt::LowerHex::fmt(&self.0, f) +// } +// } impl FromStr for Signature { type Err = SignatureError; fn from_str(value: &str) -> Result { - if let Some(hex_str) = value.strip_prefix("sig:") { - Signature::from_str_no_prefix(hex_str) - } else { - Err(SignatureError::InvalidPrefix(value.to_string())) + let signature = Ed25519Signature::from_str(value).map(Signature)?; + + match signature.validate_r_point() { + true => Ok(signature), + false => Err(SignatureError::CorruptRPointStr(value.to_string())), } } } @@ -140,8 +127,8 @@ mod tests { use super::*; use serde_json; - const VALID_STR: &str = "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; - const VALID_JSON_STR: &str = r#""sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#; + const VALID_STR: &str = "f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + const VALID_JSON_STR: &str = r#""f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#; fn valid_signature() -> Signature { Signature::from_str(VALID_STR).unwrap() } @@ -162,93 +149,29 @@ mod tests { assert_eq!(serde_json::from_str::(VALID_JSON_STR).unwrap(), valid_signature()); } - fn test_deserialize_missing_prefix() { - let err = serde_json::from_str::(r#""f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902""#).expect_err("no prefix"); - let mystr = format!("{:?}", err); - assert!(mystr.contains("expected a 64 byte hex string representing a ed25519 signature prefixed with 'sig:'")); - } - - fn test_missing_prefix() { - let test_case = "f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; - let err = Signature::from_str(test_case).expect_err("no prefix"); - match err { - SignatureError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_corrupt_prefix() { - let test_case = ":f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; - let err = Signature::from_str(test_case).expect_err("no prefix"); - match err { - SignatureError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_wrong_prefix() { - let test_case = "dig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; - let err = Signature::from_str(test_case).expect_err("no prefix"); - match err { - SignatureError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - fn test_invalid_hex() { - let test_case = "sig:g43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; + let test_case = "g43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; let err = Signature::from_str(test_case).expect_err("no prefix"); match err { - SignatureError::Parse(_) => (), + SignatureError::ParseSlice(_) => (), _ => panic!("unexpected error: {:?}", err), } } fn test_invalid_r_signature() { - let test_case = "sig:00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"; + let test_case = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"; let err = Signature::from_str(test_case).expect_err("no prefix"); match err { - SignatureError::CorruptRPoint(_) => (), + SignatureError::CorruptRPointStr(_) => (), _ => panic!("unexpected error: {:?}", err), } } fn test_invalid_length() { - let test_case = "sig:badc0de"; - let err = Signature::from_str(test_case).expect_err("no prefix"); - match err { - SignatureError::Parse(e) => println!("{:?}", e), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_from_str_no_prefix_valid() { - let sig = Signature::from_str_no_prefix("f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902").unwrap(); - assert_eq!(sig, valid_signature()) - } - - fn test_from_str_no_prefix_invalid_length() { let test_case = "badc0de"; - let err = Signature::from_str_no_prefix(test_case).expect_err("invalid length"); - match err { - SignatureError::Parse(_) => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_from_str_no_prefix_invalid_hex() { - let test_case = "g43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902"; - let err = Signature::from_str_no_prefix(test_case).expect_err("invalid hex"); - match err { - SignatureError::Parse(_) => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_from_str_no_prefix_invalid_has_prefix() { - let err = Signature::from_str_no_prefix(VALID_STR).expect_err("invalid hex"); + let err = Signature::from_str(test_case).expect_err("invalid length"); match err { - SignatureError::Parse(_) => (), + SignatureError::ParseSlice(_) => (), _ => panic!("unexpected error: {:?}", err), } } diff --git a/src/types/transaction.rs b/src/types/transaction.rs index e21532e..b461292 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -607,8 +607,8 @@ pub struct V2FileContract { impl V2FileContract { pub fn with_nil_sigs(&self) -> V2FileContract { V2FileContract { - renter_signature: Signature::from_bytes(&[0u8; 64]).expect("Err unreachable"), - host_signature: Signature::from_bytes(&[0u8; 64]).expect("Err unreachable"), + renter_signature: Signature::default(), + host_signature: Signature::default(), ..self.clone() } } @@ -895,15 +895,11 @@ pub struct V2FileContractRenewal { impl V2FileContractRenewal { pub fn with_nil_sigs(&self) -> V2FileContractRenewal { - debug_assert!( - Signature::from_bytes(&[0u8; 64]).is_ok(), - "nil signature is valid and cannot return Err" - ); V2FileContractRenewal { final_revision: self.final_revision.with_nil_sigs(), new_contract: self.new_contract.with_nil_sigs(), - renter_signature: Signature::from_bytes(&[0u8; 64]).expect("Err unreachable"), - host_signature: Signature::from_bytes(&[0u8; 64]).expect("Err unreachable"), + renter_signature: Signature::default(), + host_signature: Signature::default(), ..self.clone() } } From aa01bf2c0020dd7ab6f3c77b040eabb8b052713d Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 16:44:08 -0500 Subject: [PATCH 218/233] use Signature::try_from instead of now removed Signature::from_bytes --- src/tests/transaction.rs | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 1d2017e..71bce70 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -5,6 +5,7 @@ mod test { Preimage, PublicKey, SatisfiedPolicy, SiacoinElement, SiacoinInputV1, SiacoinInputV2, SiacoinOutput, SiacoinOutputId, SiacoinOutputVersion, Signature, SpendPolicy, StateElement, UnlockCondition, V2FileContract, V2FileContractElement, V2Transaction}; + use std::convert::TryFrom; use std::str::FromStr; cross_target_tests! { @@ -153,8 +154,8 @@ mod test { } fn test_signature_encode() { - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap().as_ref()).unwrap(); let hash = Encoder::encode_and_hash(&signature); let expected = Hash256::from_str("1e6952fe04eb626ae759a0090af2e701ba35ee6ad15233a2e947cb0f7ae9f7c7").unwrap(); @@ -169,8 +170,8 @@ mod test { let policy = SpendPolicy::PublicKey(public_key); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let satisfied_policy = SatisfiedPolicy { policy, @@ -206,8 +207,8 @@ mod test { let satisfied_policy = SatisfiedPolicy { policy, - signatures: vec!(Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), + signatures: vec!(Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap()), preimages: vec!(preimage.into()), }; @@ -242,8 +243,8 @@ mod test { let policy = SpendPolicy::UnlockConditions(unlock_condition); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let satisfied_policy = SatisfiedPolicy { policy, @@ -274,12 +275,12 @@ mod test { let policy = SpendPolicy::UnlockConditions(unlock_condition); - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); - let sig2 = Signature::from_bytes( - &hex::decode("482A2A905D7A6FC730387E06B45EA0CF259FCB219C9A057E539E705F60AC36D7079E26DAFB66ED4DBA9B9694B50BCA64F1D4CC4EBE937CE08A34BF642FAC1F0C").unwrap()).unwrap(); + let sig0 = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::try_from( + hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + let sig2 = Signature::try_from( + hex::decode("482A2A905D7A6FC730387E06B45EA0CF259FCB219C9A057E539E705F60AC36D7079E26DAFB66ED4DBA9B9694B50BCA64F1D4CC4EBE937CE08A34BF642FAC1F0C").unwrap()).unwrap(); let satisfied_policy = SatisfiedPolicy { policy, @@ -325,8 +326,8 @@ mod test { let secret_hash = Hash256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_success(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let mut preimage = [0u8; 32]; preimage[..4].copy_from_slice(&[1, 2, 3, 4]); @@ -355,8 +356,8 @@ mod test { let secret_hash = Hash256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); let policy = SpendPolicy::atomic_swap_refund(&alice_pubkey, &bob_pubkey, 77777777, &secret_hash); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let mut preimage = [0u8; 32]; preimage[..4].copy_from_slice(&[1, 2, 3, 4]); @@ -413,8 +414,8 @@ mod test { &hex::decode("0102030000000000000000000000000000000000000000000000000000000000").unwrap(), ) .unwrap(); - let signature = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let signature = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); let attestation = Attestation { public_key, @@ -438,10 +439,10 @@ mod test { ) .unwrap(); - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + let sig0 = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::try_from( + hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); let address0 = Address::standard_address_v1(&pubkey0); let address1 = Address::standard_address_v1(&pubkey1); @@ -487,10 +488,10 @@ mod test { ) .unwrap(); - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + let sig0 = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::try_from( + hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); let address0 = Address::standard_address_v1(&pubkey0); let address1 = Address::standard_address_v1(&pubkey1); @@ -550,10 +551,10 @@ mod test { ) .unwrap(); - let sig0 = Signature::from_bytes( - &hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); - let sig1 = Signature::from_bytes( - &hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); + let sig0 = Signature::try_from( + hex::decode("105641BF4AE119CB15617FC9658BEE5D448E2CC27C9BC3369F4BA5D0E1C3D01EBCB21B669A7B7A17CF8457189EAA657C41D4A2E6F9E0F25D0996D3A17170F309").unwrap()).unwrap(); + let sig1 = Signature::try_from( + hex::decode("0734761D562958F6A82819474171F05A40163901513E5858BFF9E4BD9CAFB04DEF0D6D345BACE7D14E50C5C523433B411C7D7E1618BE010A63C55C34A2DEE70A").unwrap()).unwrap(); let address0 = Address::standard_address_v1(&pubkey0); let address1 = Address::standard_address_v1(&pubkey1); From ab201b93fca46e9a24d776ddd70b3d9d10e165fa Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 16:44:36 -0500 Subject: [PATCH 219/233] update serde tests v2ContractResolution event expected case JSON --- src/tests/serde.rs | 121 ++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 241e7b2..b44fbff 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -197,83 +197,70 @@ mod test { } fn test_serde_event_v2_contract_resolution_storage_proof() { - let j = json!( + let j = r#" { - "id": "a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", + "id": "16406893374eb18eeea95e8c0d6b6c325275ecb99cf2fec7a6708b0b8def75bd", "index": { - "height": 201, - "id": "bid:e6e5282f107f2957844a93612e71003ec67238f32504b151e9e21fbb9224e8cf" + "height": 190, + "id": "22693d8885ad7b5e2abf22fe838fd6ae9856142f898607ffd2ddb8dd3d7ca67b" }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 345, + "confirmations": 17, "type": "v2ContractResolution", "data": { - "resolution": { - "parent": { - "id": "b30e0d25d4e414763378236b00a98cfbf9cd6a5e81540d1dcd40338ab6a5c636", - "leafIndex": 397, - "merkleProof": [ - "4d2a433de745231ff1eb0736ba68ffc3f8b1a976dbc3eca9649b5cf2dd5c2c44", - "e23fdf53d7c3c2bc7dc58660cb16e5b66dbf2e71c0a46c778af1c4d59a83cf63", - "0e63636af15d58fd9a87e21719899c2d518a948305e325929cbc4652d0fc3b38", - "37e5cee3bb2607e537209807b07dafef9658253080751b11858a9ae844364c0b", - "077252892fc0b8e687f14baf2ad3d2812539d05a293bfcabe8f0b884d8c91b01" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 200, - "expirationHeight": 210, - "renterOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "hostOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d", - "hostSignature": "sig:9d001e60633801956d1ce8b281b18a4b7da1249e8cb1e13b808f19c23e31c52596c303bd5efca278461877050412f1bec489037f101b7f41d3069906c60be30d" - } - }, - "type": "storageProof", - "resolution": { - "proofIndex": { - "id": "ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290", - "leafIndex": 416, - "merkleProof": [], - "chainIndex": { - "height": 200, - "id": "bid:ee154b9b26af5a130d189c2467bd0157f24f4357478bfe5184243ab918c20290" - } - }, - "leaf": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "proof": [] - } + "resolution": { + "parent": { + "id": "e5adb3e8e49d9bd29e54966e809cc652f08dfca2183fad00f3da29df83f65091", + "stateElement": { + "leafIndex": 351 }, - "siacoinElement": { - "id": "a863dbc4f02efdfbf9f8d03e1aada090ede0a5752b71503787617d5f395c1335", - "leafIndex": 418, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "v2FileContract": { + "capacity": 0, + "filesize": 0, + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 179, + "expirationHeight": 189, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "hostOutput": { + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, - "maturityHeight": 345 + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "88b5f53a69759264f60cb227e7d4fdb25ee185f9c9b9bcf4f6e94c413ace76e1d1dcf72d509670e3d4e89d3dccb326d9c74411909e0a2e0e7e1e18bf3acb6c0c", + "hostSignature": "88b5f53a69759264f60cb227e7d4fdb25ee185f9c9b9bcf4f6e94c413ace76e1d1dcf72d509670e3d4e89d3dccb326d9c74411909e0a2e0e7e1e18bf3acb6c0c" + } }, - "missed": false - } + "type": "expiration", + "resolution": {} + }, + "siacoinElement": { + "id": "16406893374eb18eeea95e8c0d6b6c325275ecb99cf2fec7a6708b0b8def75bd", + "stateElement": { + "leafIndex": 391 + }, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 334 + }, + "missed": true + }, + "maturityHeight": 334, + "timestamp": "2024-11-15T19:41:06Z", + "relevant": [ + "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + ] } - ); - - let _event = serde_json::from_value::(j).unwrap(); + "#; - // FIXME this should deserialize from a JSON object generated from walletd and recalcuate the txid to check encoding/serde + let _event = serde_json::from_str::(j).unwrap(); } fn test_serde_event_v2_contract_resolution_renewal() { From 3e394238b4c9f8bdb52ee3e61e6f46cdadad0806 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 16:57:35 -0500 Subject: [PATCH 220/233] manually impl Serialize Deserialize for Hash256 for better error messages --- src/types/hash.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/types/hash.rs b/src/types/hash.rs index 03f992c..ac754b9 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -1,5 +1,5 @@ use hex; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::convert::TryFrom; use std::fmt::{self, Display}; use std::str::FromStr; @@ -14,9 +14,27 @@ pub enum Hash256Error { #[error("Hash256::TryFrom<&[u8]> invalid slice length: expected 32 byte slice, found {0:?}")] InvalidSliceLength(Vec), } -#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] -#[serde(transparent)] -pub struct Hash256(#[serde(with = "hex::serde")] pub [u8; 32]); +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Hash256(pub [u8; 32]); + +impl Serialize for Hash256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Hash256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Hash256::from_str(&s).map_err(serde::de::Error::custom) + } +} impl FromStr for Hash256 { type Err = Hash256Error; From 83df4f39097820263378f5155c2d2f45b4f34989 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 17:00:05 -0500 Subject: [PATCH 221/233] remove prefix from varoius unit tests --- src/tests/serde.rs | 36 +++++++++++------------------------- src/tests/transaction.rs | 4 ++-- src/transport/client/wasm.rs | 2 +- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b44fbff..464027f 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1,17 +1,3 @@ -// // FIXME reminder to populate the following tests -// #[test] -// #[ignore] -// fn test_serde_block_id() { -// test_serde!( -// BlockID, -// json!("bid:c67c3b2e57490617a25a9fcb9fd54ab6acbe72fc1e4f1f432cb9334177917667") -// ); -// test_serde!(BlockID, json!("bid:badc0de")); -// test_serde!(BlockID, json!("bid:1badc0de")); -// test_serde!(BlockID, json!("1badc0de")); -// test_serde!(BlockID, json!(1)); -// } - #[test] #[ignore] // FIXME I don't have a good test case for this yet because wallet_test.go TestEventTypes doesn't output this type fn test_serde_event_v2_contract_resolution_finalization() { @@ -21,7 +7,7 @@ fn test_serde_event_v2_contract_resolution_finalization() { "id": "4057e021e1d6dec8d4e4ef9d6e9fa2e4491c559144848b9af5765e03b39bb69d", "index": { "height": 0, - "id": "bid:0000000000000000000000000000000000000000000000000000000000000000" + "id": "0000000000000000000000000000000000000000000000000000000000000000" }, "timestamp": "2024-07-12T10:04:18.564506-07:00", "maturityHeight": 0, @@ -55,8 +41,8 @@ fn test_serde_event_v2_contract_resolution_finalization() { "renterPublicKey": "ed25519:65ea9701c409d4457a830b6fe7a2513d6f466ab4e424b3941de9f34a4a2d6170", "hostPublicKey": "ed25519:65ea9701c409d4457a830b6fe7a2513d6f466ab4e424b3941de9f34a4a2d6170", "revisionNumber": 0, - "renterSignature": "sig:bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f", - "hostSignature": "sig:bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f" + "renterSignature": "bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f", + "hostSignature": "bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f" } }, "type": "finalization", @@ -78,8 +64,8 @@ fn test_serde_event_v2_contract_resolution_finalization() { "renterPublicKey": "ed25519:65ea9701c409d4457a830b6fe7a2513d6f466ab4e424b3941de9f34a4a2d6170", "hostPublicKey": "ed25519:65ea9701c409d4457a830b6fe7a2513d6f466ab4e424b3941de9f34a4a2d6170", "revisionNumber": 18446744073709551615u64, - "renterSignature": "sig:bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f", - "hostSignature": "sig:bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f" + "renterSignature": "bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f", + "hostSignature": "bd1794b9266fa0de94aea0f0ffb6550efd7e8874133963022413c8ccfe1a0e31c14690d3a5bbd343b160ed59219bd67f79103c45aee07f519d72b5ab4319440f" } } } @@ -386,7 +372,7 @@ mod test { "id": "4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", "index": { "height": 190, - "id": "bid:730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" + "id": "730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" }, "timestamp": "2024-07-18T19:04:16Z", "maturityHeight": 334, @@ -423,8 +409,8 @@ mod test { "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", "revisionNumber": 0, - "renterSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", - "hostSignature": "sig:c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" + "renterSignature": "c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", + "hostSignature": "c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" } }, "type": "expiration", @@ -454,7 +440,7 @@ mod test { "id": "5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", "index": { "height": 203, - "id": "bid:bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + "id": "bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" }, "timestamp": "2024-07-18T19:04:16Z", "maturityHeight": 203, @@ -488,7 +474,7 @@ mod test { } }, "signatures": [ - "sig:c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" + "c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" ] } } @@ -546,7 +532,7 @@ mod test { } }, "signatures": [ - "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" + "f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" ] } } diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs index 71bce70..3de3c25 100644 --- a/src/tests/transaction.rs +++ b/src/tests/transaction.rs @@ -645,7 +645,7 @@ mod test { } }, "signatures": [ - "sig:f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902" + "f43380794a6384e3d24d9908143c05dd37aaac8959efb65d986feb70fe289a5e26b84e0ac712af01a2f85f8727da18aae13a599a51fb066d098591e40cb26902" ] } } @@ -708,7 +708,7 @@ mod test { } }, "signatures": [ - "sig:f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" + "f0a29ba576eb0dbc3438877ac1d3a6da4f3c4cbafd9030709c8a83c2fffa64f4dd080d37444261f023af3bd7a10a9597c33616267d5371bf2c0ade5e25e61903" ] } } diff --git a/src/transport/client/wasm.rs b/src/transport/client/wasm.rs index f67de01..512f11e 100644 --- a/src/transport/client/wasm.rs +++ b/src/transport/client/wasm.rs @@ -162,7 +162,7 @@ mod wasm_tests { "policy": "ed25519:968e286ef5df3954b7189c53a0b4b3d827664357ebc85d590299b199af46abad" }, "signatures": [ - "sig:7a2c332fef3958a0486ef5e55b70d2a68514ff46d9307a85c3c0e40b76a19eebf4371ab3dd38a668cefe94dbedff2c50cc67856fbf42dce2194b380e536c1500" + "7a2c332fef3958a0486ef5e55b70d2a68514ff46d9307a85c3c0e40b76a19eebf4371ab3dd38a668cefe94dbedff2c50cc67856fbf42dce2194b380e536c1500" ] } } From 5ab78a0a14ecf8ff9835221f0e418958321a879d Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 17:16:53 -0500 Subject: [PATCH 222/233] fix V2FileContractElement field name serde --- src/types/transaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index b461292..77c1e5d 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -635,7 +635,6 @@ impl Encodable for V2FileContract { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct V2FileContractElement { - #[serde(rename = "ID")] pub id: FileContractID, pub state_element: StateElement, pub v2_file_contract: V2FileContract, From 27ee2753696779ce9931a8b1a853113caa9cdcbb Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 17:17:19 -0500 Subject: [PATCH 223/233] ignore several tests that rely on hardcoded walletd server --- src/transport/client/native.rs | 4 ++++ src/transport/client/wasm.rs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/transport/client/native.rs b/src/transport/client/native.rs index 6e38e9c..5fab584 100644 --- a/src/transport/client/native.rs +++ b/src/transport/client/native.rs @@ -140,15 +140,18 @@ mod tests { api_client.dispatcher(request).await.unwrap() } + #[ignore = "FIXME Alright must utilize docker container or mock server"] #[tokio::test] async fn test_new_client() { let _api_client = init_client().await; } + #[ignore = "FIXME Alright must utilize docker container or mock server"] #[tokio::test] async fn test_api_consensus_tip() { // paranoid unit test - NativeClient::new already pings the server with ConsensusTipRequest let _response = test_dispatch(ConsensusTipRequest).await; } + #[ignore = "FIXME Alright must utilize docker container or mock server"] #[tokio::test] async fn test_api_address_balance() { let request = AddressBalanceRequest { @@ -158,6 +161,7 @@ mod tests { let _response = test_dispatch(request).await; } + #[ignore = "FIXME Alright must utilize docker container or mock server"] #[tokio::test] async fn test_api_events() { use crate::types::Hash256; diff --git a/src/transport/client/wasm.rs b/src/transport/client/wasm.rs index 512f11e..66e495d 100644 --- a/src/transport/client/wasm.rs +++ b/src/transport/client/wasm.rs @@ -128,7 +128,8 @@ mod wasm_tests { headers: HashMap::new(), }); - #[wasm_bindgen_test] + // #[ignore] -- FIXME Alright must use docker container or mock server + // #[wasm_bindgen_test] async fn test_sia_wasm_client_client_error() { use crate::transport::endpoints::TxpoolBroadcastRequest; use crate::types::V2Transaction; From 8cd399ca3f5fefc1f84704ac8925a85738baf020 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 17:17:38 -0500 Subject: [PATCH 224/233] fix expected case in various events unit tests --- src/tests/serde.rs | 299 +++++++++++++++++++++++---------------------- 1 file changed, 154 insertions(+), 145 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 464027f..5f007d0 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -254,31 +254,31 @@ mod test { { "id": "5d565129957e1493902123f6d58775593a53ccbff1e30342defaf563853c30b4", "index": { - "height": 203, - "id": "f5674e39f155f1d5afe6cd2315a8b6c89843c1fbc19b13d8c6b3636b20cb537c" + "height": 203, + "id": "f5674e39f155f1d5afe6cd2315a8b6c89843c1fbc19b13d8c6b3636b20cb537c" }, "confirmations": 4, "type": "v2ContractResolution", "data": { - "resolution": { + "resolution": { "parent": { - "id": "d219a1300698e798338df61f6f816f593672f71bce274d5130e1ba95e1d63814", - "stateElement": { + "id": "d219a1300698e798338df61f6f816f593672f71bce274d5130e1ba95e1d63814", + "stateElement": { "leafIndex": 423 - }, - "v2FileContract": { + }, + "v2FileContract": { "capacity": 0, "filesize": 0, "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 211, "expirationHeight": 221, "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -287,23 +287,23 @@ mod test { "revisionNumber": 0, "renterSignature": "3aaf47eb60d992bced4818291eb1b7773e20a731df48857474715602db31a12fddf29170337803f6dd1ce95e1e2043714c2b3bcb99925ea37ad2cf4880922c02", "hostSignature": "3aaf47eb60d992bced4818291eb1b7773e20a731df48857474715602db31a12fddf29170337803f6dd1ce95e1e2043714c2b3bcb99925ea37ad2cf4880922c02" - } + } }, "type": "renewal", "resolution": { - "finalRevision": { + "finalRevision": { "capacity": 0, "filesize": 0, "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 211, "expirationHeight": 221, "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -312,20 +312,20 @@ mod test { "revisionNumber": 18446744073709551615, "renterSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "hostSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "newContract": { + }, + "newContract": { "capacity": 0, "filesize": 0, "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", "proofHeight": 221, "expirationHeight": 231, "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, "missedHostValue": "0", "totalCollateral": "0", @@ -334,101 +334,99 @@ mod test { "revisionNumber": 0, "renterSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "hostSignature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - }, - "renterRollover": "0", - "hostRollover": "0", - "renterSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f", - "hostSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f" + }, + "renterRollover": "0", + "hostRollover": "0", + "renterSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f", + "hostSignature": "f43d4b5d859931669f7db479af2e3064ed40cfa333b625120521f20f9cf9867b2c38d95cc2ee6f2d75e438ad6c25ce9f7b436e6ccbe70237f0b66e2d1dae720f" } - }, - "siacoinElement": { + }, + "siacoinElement": { "id": "5d565129957e1493902123f6d58775593a53ccbff1e30342defaf563853c30b4", "stateElement": { - "leafIndex": 427 + "leafIndex": 427 }, "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, "maturityHeight": 347 - }, - "missed": false + }, + "missed": false }, "maturityHeight": 347, "timestamp": "2024-11-15T19:41:06Z", "relevant": [ - "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" ] - } + } "#; let _event = serde_json::from_str::(json_str).unwrap(); } - #[ignore] // FIXME Error("expected an empty map for expiration", line: 0, column: 0) fn test_serde_event_v2_contract_resolution_expiration() { let j = json!( { - "id": "4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", + "id": "16406893374eb18eeea95e8c0d6b6c325275ecb99cf2fec7a6708b0b8def75bd", "index": { - "height": 190, - "id": "730f554f8cd5e6bd855b21b8c53f59808f3aa7351093f44da7761181283e3c6b" + "height": 190, + "id": "22693d8885ad7b5e2abf22fe838fd6ae9856142f898607ffd2ddb8dd3d7ca67b" }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 334, + "confirmations": 17, "type": "v2ContractResolution", "data": { - "resolution": { - "parent": { - "id": "34f6bb9b9ed58dedebce2f39d29a526ea3012e9ae005cfca6a5257761c5412f6", - "leafIndex": 351, - "merkleProof": [ - "e805430ecdd47bcaca574f78721c3b6a24f0a877110fc9fa7ab347fd231a9885", - "70782818a59e512d4995efd4ee94299e601496011b9c42b47eb0a3cd65aa89c9", - "42ab48d2ef2b54352d44ab2ef33c1a6d76589360c0dd556d703a452b7d3e4a2c", - "4af61bcae0a46d70f9b826b9bace336647389c38e6cb4c54356b9dd7fd6060aa", - "59d21dd10aa3def083106844e23ad7f6b93e309c80b24a03e2c9b6eba8acef33", - "f95c3f0fc4d632e5da8adcaa9249ea6b0c5fe66466a951871f5dc30a0c96b76d", - "3374baebf913a23e0b9811ae22e72f6cdf6999d332ccda4b4dbab87f58b2a574" - ], - "v2FileContract": { - "filesize": 0, - "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", - "proofHeight": 179, - "expirationHeight": 189, - "renterOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "hostOutput": { - "value": "0", - "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" - }, - "missedHostValue": "0", - "totalCollateral": "0", - "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", - "revisionNumber": 0, - "renterSignature": "c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00", - "hostSignature": "c293b22c9feee5a081699ddbf83486704df855129c2bbe27c2dc56afcb7e68cd355785fa36954471c1e48691864b240969168422b1fd6396e18f720ebec50e00" - } + "resolution": { + "parent": { + "id": "e5adb3e8e49d9bd29e54966e809cc652f08dfca2183fad00f3da29df83f65091", + "stateElement": { + "leafIndex": 351 + }, + "v2FileContract": { + "capacity": 0, + "filesize": 0, + "fileMerkleRoot": "0000000000000000000000000000000000000000000000000000000000000000", + "proofHeight": 179, + "expirationHeight": 189, + "renterOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" }, - "type": "expiration", - "resolution": {} - }, - "siacoinElement": { - "id": "4c0170b9e82eacc2d14a13b974ce0c03560358276f135403bd060b53ce53be1c", - "leafIndex": 391, - "merkleProof": null, - "siacoinOutput": { - "value": "10000000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "hostOutput": { + "value": "0", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, - "maturityHeight": 334 + "missedHostValue": "0", + "totalCollateral": "0", + "renterPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "hostPublicKey": "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc", + "revisionNumber": 0, + "renterSignature": "88b5f53a69759264f60cb227e7d4fdb25ee185f9c9b9bcf4f6e94c413ace76e1d1dcf72d509670e3d4e89d3dccb326d9c74411909e0a2e0e7e1e18bf3acb6c0c", + "hostSignature": "88b5f53a69759264f60cb227e7d4fdb25ee185f9c9b9bcf4f6e94c413ace76e1d1dcf72d509670e3d4e89d3dccb326d9c74411909e0a2e0e7e1e18bf3acb6c0c" + } }, - "missed": true - } - } + "type": "expiration", + "resolution": {} + }, + "siacoinElement": { + "id": "16406893374eb18eeea95e8c0d6b6c325275ecb99cf2fec7a6708b0b8def75bd", + "stateElement": { + "leafIndex": 391 + }, + "siacoinOutput": { + "value": "10000000000000000000000000000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 334 + }, + "missed": true + }, + "maturityHeight": 334, + "timestamp": "2024-11-15T19:41:06Z", + "relevant": [ + "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + ] + } ); let _event = serde_json::from_value::(j).unwrap(); @@ -437,61 +435,70 @@ mod test { fn test_serde_event_v2_transaction() { let j = json!( { - "id": "5900e475aace932c94bcc94cf296596ccff1d77d9aba52a079e9f429605671cd", + "id": "3203cda6aa67faca699fc9fd1e75d46cfa0ee080ddaf5485fad9bc42282a04b9", "index": { - "height": 203, - "id": "bd04c08bb96203c7f24adf2d405cb1069c7da8573573011379a986be62fc2a29" + "height": 169, + "id": "d4b10532623709b888fb6f2a2c6d865dc3d21f4d768f83c7f43814c29acf5b2b" }, - "timestamp": "2024-07-18T19:04:16Z", - "maturityHeight": 203, + "confirmations": 38, "type": "v2Transaction", "data": { - "siacoinInputs": [ + "siacoinInputs": [ { - "parent": { - "id": "78d58090bcdeaccf22abf99b6e0de25273e9eb82210359a16cefbd743a85fd50", - "leafIndex": 421, - "merkleProof": [ - "f26accb7c256e867a9ed62671ebe6c3eb34d085e5266f67073af2daa549f980d", - "d39e139147168c70da11c3f6db4fa54d35914ef67ba5654a75107da9c099ddda", - "f447a5360e1a7c4cab3062dd1699f56ea642b4f6cc6464fdfca0d1aa15fa436c" + "parent": { + "id": "a97dab89d5ba12e2c3ea852021e3be6b4472e55fc5408497d38fbfd05fd98362", + "stateElement": { + "leafIndex": 302, + "merkleProof": [ + "98c9f7eee6105d146b9374c9d7e28d8cec7ffcf95e71a33630510b90ef3b4fbb", + "e00568ef169225bb1e049e8c6435809396bee2da99595f870d834d3deb436df9", + "cd725e13fac773e43b5492ea5ffae6003ff7e3cacc4505689080fd657558a983", + "5dc34e64ffe5fdc537bc1021fbb9469e970b5a362a93acd2025215a894d1ee7f", + "9e033c9bf3664f59c573336d0d6dbf8c8a20bdf73d0ed2ce63b8cf835836ee8a", + "98fd662dfa09c67642a468d5f2d7da6a8a13a3aac74ef24a42461ec61a0f498d" + ] + }, + "siacoinOutput": { + "value": "288594172736732570239334030000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + }, + "maturityHeight": 0 + }, + "satisfiedPolicy": { + "policy": { + "type": "uc", + "policy": { + "timelock": 0, + "publicKeys": [ + "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" ], - "siacoinOutput": { - "value": "256394172736732570239334030000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" - }, - "maturityHeight": 0 + "signaturesRequired": 1 + } }, - "satisfiedPolicy": { - "policy": { - "type": "uc", - "policy": { - "timelock": 0, - "publicKeys": [ - "ed25519:cecc1507dc1ddd7295951c290888f095adb9044d1b73d696e6df065d683bd4fc" - ], - "signaturesRequired": 1 - } - }, - "signatures": [ - "c432fea5f147205e49235ddbd75c232fd8e9c7526b2b1575f70653ae2b3c0d0338c7fe710be338482060cf6ef2dea5e2319252fc28deaf70c77a2be60a533400" - ] - } + "signatures": [ + "53752750b684cab9c8c1d091f53f4dea9d1b3ab72d12d97ff73088594aa9b62198a1ed5b2fb33328075bb10f5f4a8ff14488787fc7238a174d2bc62bc96f9d07" + ] + } } - ], - "siacoinOutputs": [ + ], + "siacoinOutputs": [ { - "value": "10400000000000000000000000000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "1000000000000000000000000000", + "address": "000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69" }, { - "value": "245994172736732570239334030000", - "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + "value": "287594172736732570239334030000", + "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" } - ], - "minerFee": "0" - } - } + ], + "minerFee": "0" + }, + "maturityHeight": 169, + "timestamp": "2024-11-15T19:41:06Z", + "relevant": [ + "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" + ] + } ); test_serde!(Event, j); } @@ -503,17 +510,19 @@ mod test { { "parent": { "id": "f59e395dc5cbe3217ee80eff60585ffc9802e7ca580d55297782d4a9b4e08589", - "leafIndex": 3, - "merkleProof": [ - "ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", - "467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", - "64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", - "31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", - "12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", - "eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", - "e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", - "e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" - ], + "stateElement": { + "leafIndex": 3, + "merkleProof": [ + "ab0e1726444c50e2c0f7325eb65e5bd262a97aad2647d2816c39d97958d9588a", + "467e2be4d8482eca1f99440b6efd531ab556d10a8371a98a05b00cb284620cf0", + "64d5766fce1ff78a13a4a4744795ad49a8f8d187c01f9f46544810049643a74a", + "31d5151875152bc25d1df18ca6bbda1bef5b351e8d53c277791ecf416fcbb8a8", + "12a92a1ba87c7b38f3c4e264c399abfa28fb46274cfa429605a6409bd6d0a779", + "eda1d58a9282dbf6c3f1beb4d6c7bdc036d14a1cfee8ab1e94fabefa9bd63865", + "e03dee6e27220386c906f19fec711647353a5f6d76633a191cbc2f6dce239e89", + "e70fcf0129c500f7afb49f4f2bb82950462e952b7cdebb2ad0aa1561dc6ea8eb" + ], + }, "siacoinOutput": { "value": "300000000000000000000000000000", "address": "f7843ac265b037658b304468013da4fd0f304a1b73df0dc68c4273c867bfa38d01a7661a187f" From bec8f12c683dbf8f2ed2bbfb65edf7ea1fbdc743 Mon Sep 17 00:00:00 2001 From: Alright Date: Fri, 15 Nov 2024 17:19:06 -0500 Subject: [PATCH 225/233] cargo clippy --- src/transport/client/helpers.rs | 4 +--- src/types/hash.rs | 2 +- src/types/transaction.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 2c56bf9..4f86d81 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -301,9 +301,7 @@ pub trait ApiClientHelpers: ApiClient { v2transactions: vec![tx.clone()], }; - self.dispatcher(request) - .await - .map_err(|e| HelperError::BroadcastTx(e))?; + self.dispatcher(request).await.map_err(HelperError::BroadcastTx)?; Ok(()) } diff --git a/src/types/hash.rs b/src/types/hash.rs index ac754b9..9989a99 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -53,7 +53,7 @@ impl FromStr for Hash256 { } impl Display for Hash256 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(&self.0)) } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(self.0)) } } impl TryFrom<&[u8]> for Hash256 { diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 77c1e5d..24d3a80 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -139,7 +139,7 @@ impl Serialize for Preimage { S: Serializer, { // Use hex::encode to convert the byte array to a lowercase hex string - let hex_string = hex::encode(&self.0); + let hex_string = hex::encode(self.0); serializer.serialize_str(&hex_string) } } From 5c562e8f81a2e58e905c4c2dd79ebb2b9b5d6ee1 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 18 Nov 2024 18:29:25 -0500 Subject: [PATCH 226/233] add explicit ArbitraryData type and implement serde Prior to this change, ArbitraryData was being serialized like [0, 1, 2, 3] which walletd accepted just fine. However, walletd will itself serialize this as a base64 string. This went unnoticed until swaps with the newly added UUID inside arbitrary data field were performed. --- src/types/transaction.rs | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 24d3a80..31e2095 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1086,8 +1086,8 @@ pub struct V2Transaction { pub file_contract_resolutions: Vec, // TODO needs Encodable trait #[serde(skip_serializing_if = "Vec::is_empty")] pub attestations: Vec, - #[serde(skip_serializing_if = "Vec::is_empty")] - pub arbitrary_data: Vec, + #[serde(skip_serializing_if = "ArbitraryData::is_empty")] + pub arbitrary_data: ArbitraryData, #[serde(skip_serializing_if = "Option::is_none")] pub new_foundation_address: Option
, pub miner_fee: Currency, @@ -1165,7 +1165,7 @@ impl Encodable for V2Transaction { att.encode(encoder); } - encoder.write_len_prefixed_bytes(&self.arbitrary_data); + self.arbitrary_data.encode(encoder); encoder.write_bool(self.new_foundation_address.is_some()); match &self.new_foundation_address { @@ -1190,6 +1190,50 @@ pub enum FeePolicy { HastingsFixed(Currency), } +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ArbitraryData(pub Vec); + +impl ArbitraryData { + pub fn is_empty(&self) -> bool { self.0.is_empty() } +} + +impl Encodable for ArbitraryData { + fn encode(&self, encoder: &mut Encoder) { encoder.write_len_prefixed_bytes(&self.0); } +} + +impl Serialize for ArbitraryData { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(&base64.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ArbitraryData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ArbitraryDataVisitor; + + impl<'de> serde::de::Visitor<'de> for ArbitraryDataVisitor { + type Value = ArbitraryData; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a base64 encoded string") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + let decoded = base64.decode(value).map_err(serde::de::Error::custom)?; + Ok(ArbitraryData(decoded)) + } + } + + deserializer.deserialize_str(ArbitraryDataVisitor) + } +} + #[derive(Clone, Debug)] pub struct V2TransactionBuilder { pub siacoin_inputs: Vec, @@ -1200,7 +1244,7 @@ pub struct V2TransactionBuilder { pub file_contract_revisions: Vec, pub file_contract_resolutions: Vec, pub attestations: Vec, - pub arbitrary_data: Vec, + pub arbitrary_data: ArbitraryData, pub new_foundation_address: Option
, pub miner_fee: Currency, // fee_policy is not part Sia consensus and it not encoded into any resulting transaction. @@ -1253,7 +1297,7 @@ impl Encodable for V2TransactionBuilder { att.encode(encoder); } - encoder.write_len_prefixed_bytes(&self.arbitrary_data); + self.arbitrary_data.encode(encoder); encoder.write_bool(self.new_foundation_address.is_some()); match &self.new_foundation_address { @@ -1283,7 +1327,7 @@ impl V2TransactionBuilder { file_contract_revisions: Vec::new(), file_contract_resolutions: Vec::new(), attestations: Vec::new(), - arbitrary_data: Vec::new(), + arbitrary_data: ArbitraryData::default(), new_foundation_address: None, miner_fee: Currency::ZERO, fee_policy: None, @@ -1330,7 +1374,7 @@ impl V2TransactionBuilder { self } - pub fn arbitrary_data(&mut self, data: Vec) -> &mut Self { + pub fn arbitrary_data(&mut self, data: ArbitraryData) -> &mut Self { self.arbitrary_data = data; self } From 1f73fc6d50c03c2e7314f3c5429785e39aca5e1e Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 18 Nov 2024 18:32:32 -0500 Subject: [PATCH 227/233] add ArbitraryData roundtrip serde unit test --- src/tests/serde.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 5f007d0..7abd2a0 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -565,5 +565,64 @@ mod test { let tx2 = serde_json::from_str::(&j2).unwrap(); assert_eq!(tx, tx2); } + + fn test_v2_transaction_serde_with_arbitrary_data() { + let j = r#" + { + "siacoinInputs": [ + { + "parent": { + "id": "3d91fd82c8b197f7c63a803a5b6b15feeeb55f03a861036832137a2cd43175c5", + "stateElement": { + "leafIndex": 445, + "merkleProof": [ + "550179f319d4702c067891cc412ac3adfdbaab268285ad9e94ec8d523bda769a", + "7b0a8ab5a6fe6466ef509fc645583d128a6a20b5cff710dc7092647c9833240e", + "02f359ce3b8c053e336afbc53c1b5e11bee75b73c40d809802ab0c454b8bf556", + "d3ec699dff3207f0dc458b715b01e60a51561820ad3260a0f5857fb24d238137", + "16a007608da1f582c2901eb9b49e3a00456ce4f1c3290ad0125e59be83de19bd", + "94cdee3fd437d058d14fa43faedb8a2c6d260c8da968cee5f4293e0296f9ddfc", + "549e313b749f317300a7bb7b92545c9fb929ba5b9466f56c3151f0c180a9d7f6", + "cd0b57da0c99b37bba96270250c0c2e883655b41443d88e0e6780b420e8c5616", + "d709b55266c068cac2ca645c39a1ecce14fc30677d5855074a290e9e435b39a5" + ] + }, + "siacoinOutput": { + "value": "300000000000000000000000000000", + "address": "8b7201e203c8b4a58e2f68d29e6cb8973706f1578a074f5b29b1ae1f4136da85ae7f7667a714" + }, + "maturityHeight": 229 + }, + "satisfiedPolicy": { + "policy": { + "type": "pk", + "policy": "ed25519:32f8eb30eab7b9c8ec8be44f37ac0a2b0f7fa1f7c9540abb6e21267b1995024f" + }, + "signatures": [ + "4639d5738969c4870307d6bae35e16a1e38417a66e041aea639cdfb97ae250c5c583967e6367c5eb976c3e036792a6e0a2bcb053bfef86789a35b468bfc01b0c" + ] + } + } + ], + "siacoinOutputs": [ + { + "value": "12870012870012870012", + "address": "0125788b383a1dd122cd511386dd2668c62e54610cc743307d7a8ba17161a175f4b03c40b656" + }, + { + "value": "299999999977129987129987129988", + "address": "8b7201e203c8b4a58e2f68d29e6cb8973706f1578a074f5b29b1ae1f4136da85ae7f7667a714" + } + ], + "arbitraryData": "hss62dS2RraC7PbMFY6ETQ==", + "minerFee": "10000000000000000000" + }"#; + + let tx = serde_json::from_str::(j).unwrap(); + let j2 = serde_json::to_value(&tx).unwrap().to_string(); + let tx2 = serde_json::from_str::(&j2).unwrap(); + assert_eq!(tx, tx2); + } + } } From 9dfee2bf5d475654ad541ec838dfd13ce65014d9 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 18 Nov 2024 18:41:53 -0500 Subject: [PATCH 228/233] derive From, Into for ArbitraryData newtype --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 31e2095..f6f3784 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1190,7 +1190,7 @@ pub enum FeePolicy { HastingsFixed(Currency), } -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, From, Into)] pub struct ArbitraryData(pub Vec); impl ArbitraryData { From 5a3e16e5b484b424b4d103effe14920aacaefdd1 Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 18 Nov 2024 21:46:27 -0500 Subject: [PATCH 229/233] fix ConsensusTipstateResponse serde --- src/transport/endpoints.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 019b7de..6619d86 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -128,6 +128,7 @@ impl SiaApiRequest for ConsensusTipstateRequest { } #[derive(Clone, Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] pub struct ConsensusTipstateResponse { pub index: ChainIndex, pub prev_timestamps: Vec>, From d9ae7213f18f731a076ee8880b317f5c1f53ce8f Mon Sep 17 00:00:00 2001 From: Alright Date: Mon, 18 Nov 2024 21:47:04 -0500 Subject: [PATCH 230/233] allow unknown fields in V2Transaction serde because walletd will sometimes include "id" field --- src/types/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/transaction.rs b/src/types/transaction.rs index f6f3784..f620546 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -1068,7 +1068,7 @@ impl Encodable for V1TransactionSansSigs { } #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)] -#[serde(default, deny_unknown_fields, rename_all = "camelCase")] +#[serde(default, rename_all = "camelCase")] pub struct V2Transaction { #[serde(skip_serializing_if = "Vec::is_empty")] pub siacoin_inputs: Vec, From 9a0940ffe82af4ec106db95939d8d0464af3003d Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 19 Nov 2024 16:19:49 -0500 Subject: [PATCH 231/233] fix trailing ? on endpoint Urls that have query params but none are provided --- src/transport/client.rs | 7 +++++-- src/transport/endpoints.rs | 13 ++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/transport/client.rs b/src/transport/client.rs index 741c003..85d5640 100644 --- a/src/transport/client.rs +++ b/src/transport/client.rs @@ -69,6 +69,7 @@ pub enum ApiClientError { // Not all client implementations will have an exact equivalent of HTTP methods // However, the client implementation should be able to map the HTTP methods to its own methods +#[derive(Clone, Debug)] pub enum SchemaMethod { Get, Post, @@ -87,6 +88,7 @@ impl From for http::Method { } } +#[derive(Clone, Debug)] pub struct EndpointSchema { pub path_schema: String, // The endpoint path template (e.g., /api/transactions/{id}) pub path_params: Option>, // Optional parameters to replace in the path (e.g., /{key} becomes /value) @@ -119,8 +121,8 @@ impl EndpointSchemaBuilder { self } - pub fn query_params(mut self, query_params: HashMap) -> Self { - self.query_params = Some(query_params); + pub fn query_params(mut self, query_params: Option>) -> Self { + self.query_params = query_params; self } @@ -140,6 +142,7 @@ impl EndpointSchemaBuilder { } } +#[derive(Clone, Debug)] pub enum Body { Utf8(String), Json(JsonValue), diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 6619d86..2695495 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -173,10 +173,12 @@ impl SiaApiRequest for ConsensusUpdatesRequest { query_params.insert("limit".to_owned(), limit.to_string()); } + let query_params_option = (!query_params.is_empty()).then_some(query_params); + Ok( EndpointSchemaBuilder::new(ENDPOINT_CONSENSUS_UPDATES.to_owned(), SchemaMethod::Get) .path_params(path_params) // Set the path params containing the height and hash - .query_params(query_params) // Set the query params for ?limit= + .query_params(query_params_option) // Set the query params for ?limit= .build(), ) } @@ -323,10 +325,12 @@ impl SiaApiRequest for AddressesEventsRequest { query_params.insert("offset".to_owned(), offset.to_string()); } + let query_params_option = (!query_params.is_empty()).then_some(query_params); + Ok( EndpointSchemaBuilder::new(ENDPOINT_ADDRESSES_EVENTS.to_owned(), SchemaMethod::Get) .path_params(path_params) // Set the path params containing the address - .query_params(query_params) // Set the query params for limit and offset + .query_params(query_params_option) // Set the query params for limit and offset .build(), ) } @@ -371,6 +375,7 @@ impl SiaApiRequest for GetAddressUtxosRequest { path_params.insert("address".to_owned(), self.address.to_string()); let mut query_params = HashMap::new(); + if let Some(limit) = self.limit { query_params.insert("limit".to_owned(), limit.to_string()); } @@ -378,10 +383,12 @@ impl SiaApiRequest for GetAddressUtxosRequest { query_params.insert("offset".to_owned(), offset.to_string()); } + let query_params_option = (!query_params.is_empty()).then_some(query_params); + Ok( EndpointSchemaBuilder::new(ENDPOINT_ADDRESSES_UTXOS_SIACOIN.to_owned(), SchemaMethod::Get) .path_params(path_params) // Set the path params containing the address - .query_params(query_params) // Set the query params for limit and offset + .query_params(query_params_option) // Set the query params for limit and offset .build(), ) } From abe0f50bbc2be49bee4f517103d6e3bcd524ec9f Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 19 Nov 2024 16:23:53 -0500 Subject: [PATCH 232/233] increase native client timeout from 10 seconds to 30 --- src/transport/client/native.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transport/client/native.rs b/src/transport/client/native.rs index 5fab584..78da295 100644 --- a/src/transport/client/native.rs +++ b/src/transport/client/native.rs @@ -40,7 +40,7 @@ impl ApiClient for NativeClient { HeaderValue::from_str(&auth_value).map_err(|e| ApiClientError::BuildError(e.to_string()))?, ); } - let timeout = conf.timeout.unwrap_or(10); + let timeout = conf.timeout.unwrap_or(30); let client = ReqwestClient::builder() .default_headers(headers) .timeout(Duration::from_secs(timeout)) From 13065a465b2143c0b47e7dd3dd6c604ffec88152 Mon Sep 17 00:00:00 2001 From: Alright Date: Tue, 19 Nov 2024 16:28:19 -0500 Subject: [PATCH 233/233] add ApiClientHelpers::get_unconfirmed_transaction and dev comments related to ApiClientHelpers::get_transaction --- src/transport/client/helpers.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/transport/client/helpers.rs b/src/transport/client/helpers.rs index 4f86d81..dd3db78 100644 --- a/src/transport/client/helpers.rs +++ b/src/transport/client/helpers.rs @@ -2,7 +2,8 @@ use super::{ApiClient, ApiClientError}; use crate::transport::endpoints::{AddressBalanceRequest, AddressBalanceResponse, AddressesEventsRequest, ConsensusIndexRequest, ConsensusTipRequest, ConsensusTipstateRequest, ConsensusTipstateResponse, ConsensusUpdatesRequest, ConsensusUpdatesResponse, - GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest}; + GetAddressUtxosRequest, GetEventRequest, TxpoolBroadcastRequest, + TxpoolTransactionsRequest}; use crate::types::{Address, Currency, Event, EventDataWrapper, Hash256, PublicKey, SiacoinElement, SiacoinOutputId, SpendPolicy, TransactionId, V2Transaction, V2TransactionBuilder}; use async_trait::async_trait; @@ -14,6 +15,8 @@ pub enum HelperError { UtxoFromTxid(#[from] UtxoFromTxidError), #[error("ApiClientHelpers::get_transaction failed: {0}")] GetTx(#[from] GetTransactionError), + #[error("ApiClientHelpers::get_unconfirmed_transaction: failed to fetch mempool {0}")] + GetUnconfirmedTx(#[from] ApiClientError), #[error("ApiClientHelpers::select_unspent_outputs failed: {0}")] SelectUtxos(#[from] SelectUtxosError), #[error("ApiClientHelpers::get_event failed to fetch event: {0}")] @@ -266,6 +269,10 @@ pub trait ApiClientHelpers: ApiClient { self.dispatcher(request).await.map_err(HelperError::GetAddressEvents) } + /// Fetch a v2 transaction from the blockchain + // FIXME Alright - this should return a Result, HelperError> to allow for + // logic to handle the case where the transaction is not found in the blockchain + // ApiClientError must be refactored to allow this async fn get_transaction(&self, txid: &TransactionId) -> Result { let event = self .dispatcher(GetEventRequest { txid: txid.clone() }) @@ -277,6 +284,19 @@ pub trait ApiClientHelpers: ApiClient { } } + /// Fetch a v2 transaction from the transaction pool / mempool + /// Returns Ok(None) if the transaction is not found in the mempool + async fn get_unconfirmed_transaction(&self, txid: &TransactionId) -> Result, HelperError> { + let found_in_mempool = self + .dispatcher(TxpoolTransactionsRequest) + .await + .map_err(HelperError::GetUnconfirmedTx)? + .v2transactions + .into_iter() + .find(|tx| tx.txid() == *txid); + Ok(found_in_mempool) + } + /// Get the median timestamp of the chain's last 11 blocks /// This is used in the evaluation of SpendPolicy::After async fn get_median_timestamp(&self) -> Result {