diff --git a/fortuna/Cargo.lock b/fortuna/Cargo.lock index b5827ced..726b7a8d 100644 --- a/fortuna/Cargo.lock +++ b/fortuna/Cargo.lock @@ -58,6 +58,21 @@ dependencies = [ "memchr", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -511,7 +526,11 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", + "iana-time-zone", "num-traits", + "serde", + "windows-targets", ] [[package]] @@ -750,6 +769,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.38", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.38", +] + [[package]] name = "data-encoding" version = "2.4.0" @@ -771,6 +825,9 @@ name = "deranged" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", +] [[package]] name = "derive_more" @@ -1407,6 +1464,7 @@ dependencies = [ "serde", "serde_json", "serde_qs", + "serde_with", "serde_yaml", "sha3", "tokio", @@ -1787,6 +1845,35 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[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.4.0" @@ -1855,6 +1942,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -3270,6 +3358,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.4", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.0.2", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "serde_yaml" version = "0.9.25" @@ -4263,6 +4380,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/fortuna/Cargo.toml b/fortuna/Cargo.toml index 5eb8fd4b..3128fd1c 100644 --- a/fortuna/Cargo.toml +++ b/fortuna/Cargo.toml @@ -20,6 +20,7 @@ reqwest = { version = "0.11.22", features = ["json", "blocking"] } serde = { version = "1.0.188", features = ["derive"] } serde_qs = { version = "0.12.0", features = ["axum"] } serde_json = "1.0.107" +serde_with = { version = "3.4.0", features = ["hex", "base64"] } serde_yaml = "0.9.25" sha3 = "0.10.8" tokio = { version = "1.33.0", features = ["full"] } diff --git a/fortuna/src/api/revelation.rs b/fortuna/src/api/revelation.rs index 2f7296f6..205d3997 100644 --- a/fortuna/src/api/revelation.rs +++ b/fortuna/src/api/revelation.rs @@ -8,18 +8,19 @@ use { axum::{ extract::{ Path, + Query, State, }, Json, }, pythnet_sdk::wire::array, + serde_with::serde_as, utoipa::{ IntoParams, ToSchema, }, }; -// TODO: this should probably take path parameters /v1/revelation// /// Reveal the random value for a given sequence number and blockchain. /// /// Given a sequence number, retrieve the corresponding random value that this provider has committed to. @@ -34,11 +35,12 @@ responses( (status = 200, description = "Random value successfully retrieved", body = GetRandomValueResponse), (status = 403, description = "Random value cannot currently be retrieved", body = String) ), -params(GetRandomValueQueryParams) +params(RevelationPathParams, RevelationQueryParams) )] pub async fn revelation( State(state): State, - Path(GetRandomValueQueryParams { chain_id, sequence }): Path, + Path(RevelationPathParams { chain_id, sequence }): Path, + Query(RevelationQueryParams { encoding }): Query, ) -> Result, RestError> { state .metrics @@ -70,8 +72,10 @@ pub async fn revelation( .state .reveal(sequence) .map_err(|_| RestError::Unknown)?; + let encoded_value = Blob::new(encoding.unwrap_or(BinaryEncoding::Hex), value.clone()); + Ok(Json(GetRandomValueResponse { - value: (*value).clone(), + value: encoded_value, })) } else { Err(RestError::NoPendingRequest) @@ -80,15 +84,67 @@ pub async fn revelation( #[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)] #[into_params(parameter_in=Path)] -pub struct GetRandomValueQueryParams { +pub struct RevelationPathParams { #[param(value_type = String)] pub chain_id: ChainId, pub sequence: u64, } +#[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)] +#[into_params(parameter_in=Query)] +pub struct RevelationQueryParams { + pub encoding: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(rename_all = "kebab-case")] +pub enum BinaryEncoding { + #[serde(rename = "hex")] + Hex, + #[serde(rename = "base64")] + Base64, + #[serde(rename = "array")] + Array, +} + #[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)] pub struct GetRandomValueResponse { // TODO: choose serialization format - #[serde(with = "array")] - pub value: [u8; 32], + pub value: Blob, +} + +#[serde_as] +#[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(tag = "encoding", rename_all = "kebab-case")] +pub enum Blob { + Hex { + #[serde_as(as = "serde_with::hex::Hex")] + data: [u8; 32], + }, + Base64 { + #[serde_as(as = "serde_with::base64::Base64")] + data: [u8; 32], + }, + Array { + #[serde(with = "array")] + data: [u8; 32], + }, +} + +impl Blob { + pub fn new(encoding: BinaryEncoding, data: [u8; 32]) -> Blob { + match encoding { + BinaryEncoding::Hex => Blob::Hex { data }, + BinaryEncoding::Base64 => Blob::Base64 { data }, + BinaryEncoding::Array => Blob::Array { data }, + } + } + + pub fn data(&self) -> &[u8; 32] { + match self { + Blob::Hex { data } => data, + Blob::Base64 { data } => data, + Blob::Array { data } => data, + } + } } diff --git a/fortuna/src/command/generate.rs b/fortuna/src/command/generate.rs index cef11589..e59cad75 100644 --- a/fortuna/src/command/generate.rs +++ b/fortuna/src/command/generate.rs @@ -45,10 +45,10 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { .await?; tracing::info!( - response = base64_standard_engine.encode(resp.value), + response = base64_standard_engine.encode(resp.value.data()), "Retrieved the provider's random value.", ); - let provider_randomness = resp.value; + let provider_randomness = resp.value.data(); // Submit the provider's and our values to the contract to reveal the random number. let random_value = contract diff --git a/fortuna/src/command/run.rs b/fortuna/src/command/run.rs index 16b38b52..12b22cec 100644 --- a/fortuna/src/command/run.rs +++ b/fortuna/src/command/run.rs @@ -38,6 +38,8 @@ pub async fn run(opts: &RunOptions) -> Result<()> { components( schemas( crate::api::GetRandomValueResponse, + crate::api::Blob, + crate::api::BinaryEncoding, ) ), tags(