diff --git a/Cargo.lock b/Cargo.lock index 2f0d09be9..8580a36aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4958,7 +4958,7 @@ dependencies = [ [[package]] name = "witnet" -version = "1.6.7" +version = "2.0.0" dependencies = [ "ansi_term", "bytecount", @@ -5005,7 +5005,7 @@ dependencies = [ [[package]] name = "witnet-centralized-ethereum-bridge" -version = "1.6.7" +version = "2.0.0" dependencies = [ "actix", "async-jsonrpc-client", @@ -5069,7 +5069,7 @@ dependencies = [ [[package]] name = "witnet_data_structures" -version = "1.6.7" +version = "2.0.0" dependencies = [ "bech32", "bencher", @@ -5134,7 +5134,7 @@ dependencies = [ [[package]] name = "witnet_node" -version = "1.6.7" +version = "2.0.0" dependencies = [ "actix", "ansi_term", @@ -5198,7 +5198,7 @@ dependencies = [ [[package]] name = "witnet_rad" -version = "0.3.2" +version = "0.3.3" dependencies = [ "cbor-codec", "failure", @@ -5241,7 +5241,7 @@ dependencies = [ [[package]] name = "witnet_toolkit" -version = "1.6.7" +version = "2.0.0" dependencies = [ "failure", "hex", @@ -5284,7 +5284,7 @@ dependencies = [ [[package]] name = "witnet_wallet" -version = "1.6.7" +version = "2.0.0" dependencies = [ "actix", "async-jsonrpc-client", diff --git a/Cargo.toml b/Cargo.toml index 0a428c2fb..21a934c11 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witnet" -version = "1.6.7" +version = "2.0.0" authors = ["Witnet Foundation "] publish = false repository = "witnet/witnet-rust" diff --git a/bridges/centralized-ethereum/Cargo.toml b/bridges/centralized-ethereum/Cargo.toml index 5492feb9e..6e3a4f7a0 100644 --- a/bridges/centralized-ethereum/Cargo.toml +++ b/bridges/centralized-ethereum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witnet-centralized-ethereum-bridge" -version = "1.6.7" +version = "2.0.0" authors = ["Witnet Foundation "] edition = "2018" diff --git a/data_structures/Cargo.toml b/data_structures/Cargo.toml index fa801502b..9fd896c71 100644 --- a/data_structures/Cargo.toml +++ b/data_structures/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Witnet Foundation "] description = "data structures component" edition = "2021" name = "witnet_data_structures" -version = "1.6.7" +version = "2.0.0" workspace = ".." [features] diff --git a/data_structures/src/chain/mod.rs b/data_structures/src/chain/mod.rs index 34910b8a5..8b9b3d729 100644 --- a/data_structures/src/chain/mod.rs +++ b/data_structures/src/chain/mod.rs @@ -1645,11 +1645,17 @@ pub enum RADType { /// HTTP POST request #[serde(rename = "HTTP-POST")] HttpPost, + /// HTTP HEAD request + #[serde(rename = "HTTP-HEAD")] + HttpHead, } impl RADType { pub fn is_http(&self) -> bool { - matches!(self, RADType::HttpGet | RADType::HttpPost) + matches!( + self, + RADType::HttpGet | RADType::HttpPost | RADType::HttpHead + ) } } @@ -1701,7 +1707,7 @@ pub struct RADRetrieve { pub script: Vec, /// Body of a HTTP-POST request pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-POST or HTTP-HEAD request pub headers: Vec<(String, String)>, } @@ -1810,6 +1816,9 @@ impl RADRetrieve { &[Field::Body, Field::Headers], ) } + RADType::HttpHead => { + check(&[Field::Kind, Field::Url, Field::Script], &[Field::Headers]) + } } } @@ -2681,7 +2690,7 @@ impl TransactionsPool { for input in &vt_tx.body.inputs { self.output_pointer_map .entry(input.output_pointer) - .or_insert_with(Vec::new) + .or_default() .push(vt_tx.hash()); } @@ -2706,7 +2715,7 @@ impl TransactionsPool { for input in &dr_tx.body.inputs { self.output_pointer_map .entry(input.output_pointer) - .or_insert_with(Vec::new) + .or_default() .push(dr_tx.hash()); } diff --git a/data_structures/src/data_request.rs b/data_structures/src/data_request.rs index 6dd3493a1..fc3823037 100644 --- a/data_structures/src/data_request.rs +++ b/data_structures/src/data_request.rs @@ -143,7 +143,7 @@ impl DataRequestPool { self.data_requests_by_epoch .entry(epoch) - .or_insert_with(HashSet::new) + .or_default() .insert(dr_hash); self.data_request_pool.insert(dr_hash, dr_state); diff --git a/data_structures/src/proto/mod.rs b/data_structures/src/proto/mod.rs index 4d681d8b9..c1bb007ca 100644 --- a/data_structures/src/proto/mod.rs +++ b/data_structures/src/proto/mod.rs @@ -51,6 +51,7 @@ impl ProtobufConvert for chain::RADType { chain::RADType::HttpGet => witnet::DataRequestOutput_RADRequest_RADType::HttpGet, chain::RADType::Rng => witnet::DataRequestOutput_RADRequest_RADType::Rng, chain::RADType::HttpPost => witnet::DataRequestOutput_RADRequest_RADType::HttpPost, + chain::RADType::HttpHead => witnet::DataRequestOutput_RADRequest_RADType::HttpHead, } } @@ -60,6 +61,7 @@ impl ProtobufConvert for chain::RADType { witnet::DataRequestOutput_RADRequest_RADType::HttpGet => chain::RADType::HttpGet, witnet::DataRequestOutput_RADRequest_RADType::Rng => chain::RADType::Rng, witnet::DataRequestOutput_RADRequest_RADType::HttpPost => chain::RADType::HttpPost, + witnet::DataRequestOutput_RADRequest_RADType::HttpHead => chain::RADType::HttpHead, }) } } diff --git a/data_structures/src/radon_error.rs b/data_structures/src/radon_error.rs index 7d3176629..6fdf30d26 100644 --- a/data_structures/src/radon_error.rs +++ b/data_structures/src/radon_error.rs @@ -34,6 +34,8 @@ pub enum RadonErrors { HTTPError = 0x30, /// Al least one of the sources could not be retrieved, timeout reached. RetrieveTimeout = 0x31, + /// Value cannot be extracted from binary buffer + BufferIsNotValue = 0x32, // Math errors /// Math operator caused an underflow. Underflow = 0x40, diff --git a/data_structures/src/serialization_helpers.rs b/data_structures/src/serialization_helpers.rs index 7f7d7d4e6..a8d59ee69 100644 --- a/data_structures/src/serialization_helpers.rs +++ b/data_structures/src/serialization_helpers.rs @@ -360,7 +360,7 @@ struct RADRetrieveSerializationHelperJson { /// Body of a HTTP-POST request #[serde(default, skip_serializing_if = "Vec::is_empty")] pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-HEAD or HTTP-POST request #[serde(default, skip_serializing_if = "Vec::is_empty")] pub headers: Vec<(String, String)>, } @@ -377,7 +377,7 @@ struct RADRetrieveSerializationHelperBincode { pub script: Vec, /// Body of a HTTP-POST request pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-HEAD or HTTP-POST request pub headers: Vec<(String, String)>, } diff --git a/net/src/client/http/mod.rs b/net/src/client/http/mod.rs index 55ae33e9e..3fca5533b 100644 --- a/net/src/client/http/mod.rs +++ b/net/src/client/http/mod.rs @@ -151,7 +151,7 @@ pub struct WitnetHttpResponse { impl WitnetHttpResponse { #[inline] - /// Simple wrapper around `isahc::Response::status`. + /// Simple wrapper around `isahc::Response`. pub fn inner(self) -> isahc::Response { self.res } diff --git a/node/Cargo.toml b/node/Cargo.toml index 796ca2737..57fe55a7a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witnet_node" -version = "1.6.7" +version = "2.0.0" authors = ["Witnet Foundation "] workspace = ".." description = "node component" diff --git a/rad/Cargo.toml b/rad/Cargo.toml index 806b62527..d9bef65f3 100644 --- a/rad/Cargo.toml +++ b/rad/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witnet_rad" -version = "0.3.2" +version = "0.3.3" authors = ["Witnet Foundation "] edition = "2021" workspace = ".." diff --git a/rad/src/error.rs b/rad/src/error.rs index a413f47c4..b9f2086b6 100644 --- a/rad/src/error.rs +++ b/rad/src/error.rs @@ -431,6 +431,10 @@ impl RadError { } Ok(RadonError::new(match kind { + RadonErrors::BufferIsNotValue => { + let (description,) = deserialize_args(error_args)?; + RadError::BufferIsNotValue { description } + } RadonErrors::RequestTooManySources => RadError::RequestTooManySources, RadonErrors::ScriptTooManyCalls => RadError::ScriptTooManyCalls, RadonErrors::Overflow => RadError::Overflow, @@ -574,6 +578,7 @@ impl RadError { pub fn try_into_error_code(&self) -> Result { Ok(match self { RadError::Unknown => RadonErrors::Unknown, + RadError::BufferIsNotValue { .. } => RadonErrors::BufferIsNotValue, RadError::SourceScriptNotCBOR => RadonErrors::SourceScriptNotCBOR, RadError::SourceScriptNotArray => RadonErrors::SourceScriptNotArray, RadError::SourceScriptNotRADON => RadonErrors::SourceScriptNotRADON, diff --git a/rad/src/lib.rs b/rad/src/lib.rs index 64c826780..e0d2a5459 100644 --- a/rad/src/lib.rs +++ b/rad/src/lib.rs @@ -3,6 +3,7 @@ extern crate witnet_data_structures; use futures::{executor::block_on, future::join_all, AsyncReadExt}; +use script::RadonScript; use serde::Serialize; pub use serde_cbor::{to_vec as cbor_to_vec, Value as CborValue}; #[cfg(test)] @@ -78,7 +79,12 @@ pub fn try_data_request( .iter() .zip(inputs.iter()) .map(|(retrieve, input)| { - run_retrieval_with_data_report(retrieve, input, &mut retrieval_context, settings) + run_retrieval_with_data_report( + retrieve, + RadonTypes::from(RadonString::from(*input)), + &mut retrieval_context, + settings, + ) }) .collect() } else { @@ -160,42 +166,37 @@ pub fn try_data_request( } } -/// Handle HTTP-GET and HTTP-POST response with data, and return a `RadonReport`. -fn string_response_with_data_report( +/// Execute Radon Script using as input the RadonTypes value deserialized from a retrieval response +fn handle_response_with_data_report( retrieve: &RADRetrieve, - response: &str, + response: RadonTypes, context: &mut ReportContext, settings: RadonScriptExecutionSettings, ) -> Result> { - let input = RadonTypes::from(RadonString::from(response)); let radon_script = unpack_radon_script(&retrieve.script)?; - - execute_radon_script(input, &radon_script, context, settings) + execute_radon_script(response, &radon_script, context, settings) } -/// Handle Rng response with data report -fn rng_response_with_data_report( - response: &str, +/// Execute Radon Script using as input the RadonScript and the RadonTypes value deserialized from a retrieval response +fn process_response_with_data_report( + response: RadonTypes, + radon_script: &RadonScript, context: &mut ReportContext, + settings: RadonScriptExecutionSettings, ) -> Result> { - let response_bytes = response.as_bytes(); - let result = RadonTypes::from(RadonBytes::from(response_bytes.to_vec())); - - Ok(RadonReport::from_result(Ok(result), context)) + execute_radon_script(response, radon_script, context, settings) } /// Run retrieval without performing any external network requests, return `Result`. pub fn run_retrieval_with_data_report( retrieve: &RADRetrieve, - response: &str, + response: RadonTypes, context: &mut ReportContext, settings: RadonScriptExecutionSettings, ) -> Result> { match retrieve.kind { - RADType::HttpGet => string_response_with_data_report(retrieve, response, context, settings), - RADType::Rng => rng_response_with_data_report(response, context), - RADType::HttpPost => { - string_response_with_data_report(retrieve, response, context, settings) + RADType::HttpGet | RADType::HttpPost | RADType::HttpHead | RADType::Rng => { + handle_response_with_data_report(retrieve, response, context, settings) } _ => Err(RadError::UnknownRetrieval), } @@ -204,7 +205,7 @@ pub fn run_retrieval_with_data_report( /// Run retrieval without performing any external network requests, return `Result`. pub fn run_retrieval_with_data( retrieve: &RADRetrieve, - response: &str, + response: RadonTypes, settings: RadonScriptExecutionSettings, active_wips: ActiveWips, ) -> Result { @@ -214,7 +215,7 @@ pub fn run_retrieval_with_data( .map(RadonReport::into_inner) } -/// Handle generic HTTP (GET/POST) response +/// Handle generic HTTP (GET/POST/HEAD) response async fn http_response( retrieve: &RADRetrieve, context: &mut ReportContext, @@ -229,6 +230,9 @@ async fn http_response( })? }; + // Validate the retrieval's radon script before performing the http request + let radon_script: RadonScript = unpack_radon_script(&retrieve.script)?; + // Use the provided HTTP client, or instantiate a new one if none let client = match client { Some(client) => client, @@ -259,6 +263,10 @@ async fn http_response( WitnetHttpBody::from(retrieve.body.clone()), ) } + RADType::HttpHead => ( + builder.method("HEAD").uri(&retrieve.url), + WitnetHttpBody::empty(), + ), _ => panic!( "Called http_response with invalid retrieval kind {:?}", retrieve.kind @@ -296,18 +304,41 @@ async fn http_response( }); } - // If at some point we want to support the retrieval of non-UTF8 data (e.g. raw bytes), this is - // where we need to decide how to read the response body - let (_parts, mut body) = response.into_parts(); - let mut response_string = String::default(); - body.read_to_string(&mut response_string) - .await - .map_err(|x| RadError::HttpOther { - message: x.to_string(), - })?; - - let result = run_retrieval_with_data_report(retrieve, &response_string, context, settings); - + let (parts, mut body) = response.into_parts(); + + let response: RadonTypes = match retrieve.kind { + RADType::HttpHead => RadonTypes::from(RadonString::from(format!("{:?}", parts.headers))), + RADType::HttpGet | RADType::HttpPost => { + let expect_binary_response = if let Some((first_opcode, _)) = radon_script.first() { + (0x30..0x3f).contains(&u8::from(*first_opcode)) + } else { + false + }; + if expect_binary_response { + let mut response_bytes = Vec::::default(); + match body.read_to_end(&mut response_bytes).await { + Ok(_) => RadonTypes::from(RadonBytes::from(response_bytes)), + Err(err) => { + return Err(RadError::HttpOther { + message: err.to_string(), + }); + } + } + } else { + let mut response_string = String::default(); + match body.read_to_string(&mut response_string).await { + Ok(_) => RadonTypes::from(RadonString::from(response_string)), + Err(err) => { + return Err(RadError::HttpOther { + message: err.to_string(), + }); + } + } + } + } + _ => unreachable!(), + }; + let result = process_response_with_data_report(response, &radon_script, context, settings); match &result { Ok(report) => { log::debug!( @@ -318,7 +349,6 @@ async fn http_response( } Err(e) => log::debug!("Failed result for source {}: {:?}", retrieve.url, e), } - result } @@ -354,9 +384,10 @@ pub async fn run_retrieval_report( context.set_active_wips(active_wips); match retrieve.kind { - RADType::HttpGet => http_response(retrieve, context, settings, client).await, + RADType::HttpGet | RADType::HttpHead | RADType::HttpPost => { + http_response(retrieve, context, settings, client).await + } RADType::Rng => rng_response(context, settings).await, - RADType::HttpPost => http_response(retrieve, context, settings, client).await, _ => Err(RadError::UnknownRetrieval), } } @@ -649,7 +680,7 @@ fn validate_header(name: &str, value: &str) -> Result<()> { Err(RadError::InvalidHttpHeader { name: name.to_string(), value: value.to_string(), - error: error_message.to_string(), + error: error_message, }) } else { Ok(()) @@ -829,6 +860,39 @@ mod tests { use super::*; + #[test] + fn test_run_http_head_retrieval() { + let script_r = Value::Array(vec![ + Value::Integer(RadonOpCodes::StringParseJSONMap as i128), + Value::Array(vec![ + Value::Integer(RadonOpCodes::MapGetString as i128), + Value::Text("etag".to_string()), + ]), + ]); + let packed_script_r = serde_cbor::to_vec(&script_r).unwrap(); + + let retrieve = RADRetrieve { + kind: RADType::HttpHead, + url: "https://en.wikipedia.org/static/images/icons/wikipedia.png".to_string(), + script: packed_script_r, + body: vec![], + headers: vec![], + }; + let response_string = r#"{"date": "Wed, 11 Oct 2023 15:18:42 GMT", "content-type": "image/png", "content-length": "498219", "x-origin-cache": "HIT", "last-modified": "Mon, 28 Aug 2023 13:30:41 GMT", "access-control-allow-origin": "*", "etag": "\"64eca181-79a2b\"", "expires": "Wed, 11 Oct 2023 15:28:41 GMT", "cache-control": "max-age=1800", "x-proxy-cache": "MISS", "x-github-request-id": "6750:35DB:BF8211:FEFD2B:652602FA", "via": "1.1 varnish", "x-served-by": "cache-hnd18736-HND", "x-cache": "MISS", "x-cache-hits": "0", "x-timer": "S1696989946.496383,VS0,VE487", "vary": "Accept-Encoding", "x-fastly-request-id": "118bdfd8a926cbdc781bc23079c3dc07a22d2223", "cf-cache-status": "REVALIDATED", "accept-ranges": "bytes", "report-to": "{\"endpoints\":[{\"url\":\"https:\/\/a.nel.cloudflare.com\/report\/v3?s=FlzxKRCYYN4SL0x%2FraG7ugKCqdC%2BeQqVrucvsfeDWf%2F7A0Nv9fv7TYRgU0WL4k1kbZyxt%2B04VjOyv0XK55sF37GEPwXHE%2FdXnoFlWutID762k2ktcX6hUml6oNk%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", "nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", "strict-transport-security": "max-age=0", "x-content-type-options": "nosniff", "server": "cloudflare", "cf-ray": "814813bf3a73f689-NRT", "alt-svc": "h3=\":443\"; ma=86400"}"#; + let result = run_retrieval_with_data( + &retrieve, + RadonTypes::from(RadonString::from(response_string)), + RadonScriptExecutionSettings::disable_all(), + current_active_wips(), + ) + .unwrap(); + + match result { + RadonTypes::String(_) => {} + err => panic!("Error in run_retrieval: {:?}", err), + } + } + #[test] fn test_run_retrieval() { let script_r = Value::Array(vec![ @@ -851,11 +915,10 @@ mod tests { body: vec![], headers: vec![], }; - let response = r#"{"coord":{"lon":13.41,"lat":52.52},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"base":"stations","main":{"temp":17.59,"pressure":1022,"humidity":67,"temp_min":15,"temp_max":20},"visibility":10000,"wind":{"speed":3.6,"deg":260},"rain":{"1h":0.51},"clouds":{"all":20},"dt":1567501321,"sys":{"type":1,"id":1275,"message":0.0089,"country":"DE","sunrise":1567484402,"sunset":1567533129},"timezone":7200,"id":2950159,"name":"Berlin","cod":200}"#; - + let response_string = r#"{"coord":{"lon":13.41,"lat":52.52},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"base":"stations","main":{"temp":17.59,"pressure":1022,"humidity":67,"temp_min":15,"temp_max":20},"visibility":10000,"wind":{"speed":3.6,"deg":260},"rain":{"1h":0.51},"clouds":{"all":20},"dt":1567501321,"sys":{"type":1,"id":1275,"message":0.0089,"country":"DE","sunrise":1567484402,"sunset":1567533129},"timezone":7200,"id":2950159,"name":"Berlin","cod":200}"#; let result = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -910,7 +973,7 @@ mod tests { body: vec![], headers: vec![], }; - let response = "84"; + let response_string = "84"; let expected = RadonTypes::Float(RadonFloat::from(84)); let aggregate = RADAggregate { @@ -925,7 +988,7 @@ mod tests { let retrieved = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -948,7 +1011,7 @@ mod tests { body: vec![], headers: vec![], }; - let response = "307"; + let response_string = "307"; let expected = RadonTypes::Float(RadonFloat::from(307)); let aggregate = RADAggregate { @@ -962,7 +1025,7 @@ mod tests { let retrieved = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -1001,7 +1064,7 @@ mod tests { headers: vec![], }; // This response was modified because the original was about 100KB. - let response = r#"[{"estacion_nombre":"Pza. de España","estacion_numero":4,"fecha":"03092019","hora0":{"estado":"Pasado","valor":"00008"}}]"#; + let response_string = r#"[{"estacion_nombre":"Pza. de España","estacion_numero":4,"fecha":"03092019","hora0":{"estado":"Pasado","valor":"00008"}}]"#; let expected = RadonTypes::Float(RadonFloat::from(8)); let aggregate = RADAggregate { @@ -1015,7 +1078,7 @@ mod tests { let retrieved = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -1045,7 +1108,7 @@ mod tests { body: vec![], headers: vec![], }; - let response = r#"{"PSOE":123,"PP":66,"Cs":57,"UP":42,"VOX":24,"ERC-SOBIRANISTES":15,"JxCAT-JUNTS":7,"PNV":6,"EH Bildu":4,"CCa-PNC":2,"NA+":2,"COMPROMÍS 2019":1,"PRC":1,"PACMA":0,"FRONT REPUBLICÀ":0,"BNG":0,"RECORTES CERO-GV":0,"NCa":0,"PACT":0,"ARA-MES-ESQUERRA":0,"GBAI":0,"PUM+J":0,"EN MAREA":0,"PCTE":0,"EL PI":0,"AxSI":0,"PCOE":0,"PCPE":0,"AVANT ADELANTE LOS VERDES":0,"EB":0,"CpM":0,"SOMOS REGIÓN":0,"PCPA":0,"PH":0,"UIG-SOM-CUIDES":0,"ERPV":0,"IZQP":0,"PCPC":0,"AHORA CANARIAS":0,"CxG":0,"PPSO":0,"CNV":0,"PREPAL":0,"C.Ex-C.R.Ex-P.R.Ex":0,"PR+":0,"P-LIB":0,"CILU-LINARES":0,"ANDECHA ASTUR":0,"JF":0,"PYLN":0,"FIA":0,"FE de las JONS":0,"SOLIDARIA":0,"F8":0,"DPL":0,"UNIÓN REGIONALISTA":0,"centrados":0,"DP":0,"VOU":0,"PDSJE-UDEC":0,"IZAR":0,"RISA":0,"C 21":0,"+MAS+":0,"UDT":0}"#; + let response_string = r#"{"PSOE":123,"PP":66,"Cs":57,"UP":42,"VOX":24,"ERC-SOBIRANISTES":15,"JxCAT-JUNTS":7,"PNV":6,"EH Bildu":4,"CCa-PNC":2,"NA+":2,"COMPROMÍS 2019":1,"PRC":1,"PACMA":0,"FRONT REPUBLICÀ":0,"BNG":0,"RECORTES CERO-GV":0,"NCa":0,"PACT":0,"ARA-MES-ESQUERRA":0,"GBAI":0,"PUM+J":0,"EN MAREA":0,"PCTE":0,"EL PI":0,"AxSI":0,"PCOE":0,"PCPE":0,"AVANT ADELANTE LOS VERDES":0,"EB":0,"CpM":0,"SOMOS REGIÓN":0,"PCPA":0,"PH":0,"UIG-SOM-CUIDES":0,"ERPV":0,"IZQP":0,"PCPC":0,"AHORA CANARIAS":0,"CxG":0,"PPSO":0,"CNV":0,"PREPAL":0,"C.Ex-C.R.Ex-P.R.Ex":0,"PR+":0,"P-LIB":0,"CILU-LINARES":0,"ANDECHA ASTUR":0,"JF":0,"PYLN":0,"FIA":0,"FE de las JONS":0,"SOLIDARIA":0,"F8":0,"DPL":0,"UNIÓN REGIONALISTA":0,"centrados":0,"DP":0,"VOU":0,"PDSJE-UDEC":0,"IZAR":0,"RISA":0,"C 21":0,"+MAS+":0,"UDT":0}"#; let expected = RadonTypes::Float(RadonFloat::from(123)); let aggregate = RADAggregate { @@ -1059,7 +1122,7 @@ mod tests { let retrieved = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -1100,10 +1163,10 @@ mod tests { body: vec![], headers: vec![], }; - let response = r#"{"event":{"homeTeam":{"name":"Ryazan-VDV","slug":"ryazan-vdv","gender":"F","national":false,"id":171120,"shortName":"Ryazan-VDV","subTeams":[]},"awayTeam":{"name":"Olympique Lyonnais","slug":"olympique-lyonnais","gender":"F","national":false,"id":26245,"shortName":"Lyon","subTeams":[]},"homeScore":{"current":0,"display":0,"period1":0,"normaltime":0},"awayScore":{"current":9,"display":9,"period1":5,"normaltime":9}}}"#; + let response_string = r#"{"event":{"homeTeam":{"name":"Ryazan-VDV","slug":"ryazan-vdv","gender":"F","national":false,"id":171120,"shortName":"Ryazan-VDV","subTeams":[]},"awayTeam":{"name":"Olympique Lyonnais","slug":"olympique-lyonnais","gender":"F","national":false,"id":26245,"shortName":"Lyon","subTeams":[]},"homeScore":{"current":0,"display":0,"period1":0,"normaltime":0},"awayScore":{"current":9,"display":9,"period1":5,"normaltime":9}}}"#; let retrieved = run_retrieval_with_data( &retrieve, - response, + RadonTypes::from(RadonString::from(response_string)), RadonScriptExecutionSettings::disable_all(), current_active_wips(), ) @@ -1526,6 +1589,58 @@ mod tests { } } + #[test] + fn test_try_data_request_http_get_non_ascii_header_key() { + let script_r = Value::Array(vec![]); + let packed_script_r = serde_cbor::to_vec(&script_r).unwrap(); + let body = Vec::from(String::from("")); + let headers = vec![("ñ", "value")]; + let headers = headers + .into_iter() + .map(|(a, b)| (a.to_string(), b.to_string())) + .collect(); + let request = RADRequest { + time_lock: 0, + retrieve: vec![RADRetrieve { + kind: RADType::HttpGet, + url: String::from("http://127.0.0.1"), + script: packed_script_r, + body, + headers, + }], + aggregate: RADAggregate { + filters: vec![], + reducer: RadonReducers::Mode as u32, + }, + tally: RADTally { + filters: vec![], + reducer: RadonReducers::Mode as u32, + }, + }; + let report = try_data_request( + &request, + RadonScriptExecutionSettings::enable_all(), + None, + None, + ); + let tally_result = report.tally.into_inner(); + + assert_eq!( + tally_result, + RadonTypes::RadonError( + RadonError::try_from(RadError::UnhandledIntercept { + inner: Some(Box::new(RadError::InvalidHttpHeader { + name: "ñ".to_string(), + value: "value".to_string(), + error: "invalid HTTP header name".to_string() + })), + message: None + }) + .unwrap() + ) + ); + } + #[test] fn test_try_data_request_http_post_non_ascii_header_key() { let script_r = Value::Array(vec![]); diff --git a/rad/src/operators/mod.rs b/rad/src/operators/mod.rs index 6964f2623..23e9955ba 100644 --- a/rad/src/operators/mod.rs +++ b/rad/src/operators/mod.rs @@ -1,6 +1,6 @@ use std::fmt; -use num_enum::TryFromPrimitive; +use num_enum::{IntoPrimitive, TryFromPrimitive}; use serde::Serialize; use witnet_data_structures::radon_report::ReportContext; @@ -17,7 +17,7 @@ pub mod string; /// List of RADON operators. /// **WARNING: these codes are consensus-critical.** They can be renamed but they cannot be /// re-assigned without causing a non-backwards-compatible protocol upgrade. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, TryFromPrimitive)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] pub enum RadonOpCodes { /// Only for the sake of allowing catch-alls when matching diff --git a/schemas/witnet/witnet.proto b/schemas/witnet/witnet.proto index 64b1b04e0..842009744 100644 --- a/schemas/witnet/witnet.proto +++ b/schemas/witnet/witnet.proto @@ -121,6 +121,7 @@ message DataRequestOutput { HttpGet = 1; Rng = 2; HttpPost = 3; + HttpHead = 4; } message RADFilter { uint32 op = 1; @@ -133,7 +134,7 @@ message DataRequestOutput { bytes script = 3; // Body of HTTP-POST request bytes body = 4; - // Extra headers for HTTP-GET and HTTP-POST requests + // Extra headers for HTTP-GET, HTTP-HEAD and HTTP-POST requests repeated StringPair headers = 5; } message RADAggregate { diff --git a/toolkit/Cargo.toml b/toolkit/Cargo.toml index eb4a5096d..7ac7b72d4 100644 --- a/toolkit/Cargo.toml +++ b/toolkit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "witnet_toolkit" -version = "1.6.7" +version = "2.0.0" authors = ["Adán SDPC "] edition = "2021" diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 4bf3bb766..5a7221a5f 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Witnet Foundation "] edition = "2021" name = "witnet_wallet" -version = "1.6.7" +version = "2.0.0" workspace = ".." [dependencies]