Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ tokio = "1.44.1"

[dependencies]
anyhow = "1.0.98"
base64 = "0.22.1"
cbor-codec = { git = "https://github.com/witnet/cbor-codec.git", branch = "feat/ldexpf-shim" }
futures = "0.3.31"
hex = "0.4.1"
Expand Down
18 changes: 17 additions & 1 deletion rad/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ async fn http_response(
})?
};

// Start timer for measuring resolution time
let start_ts = std::time::SystemTime::now();

// Use the provided HTTP client, or instantiate a new one if none
let client = match client {
Some(client) => client,
Expand Down Expand Up @@ -373,7 +376,20 @@ async fn http_response(
message: x.to_string(),
})?;

let result = run_retrieval_with_data_report(retrieve, &response_string, context, settings);
let result = run_retrieval_with_data_report(retrieve, &response_string, context, settings)
// override running_time within the final report including
.map(|report| {
let completion_ts = std::time::SystemTime::now();
RadonReport {
context: ReportContext {
start_time: Some(start_ts),
completion_time: Some(completion_ts),
..report.context
},
running_time: completion_ts.duration_since(start_ts).unwrap_or_default(),
..report
}
});

match &result {
Ok(report) => {
Expand Down
81 changes: 77 additions & 4 deletions rad/src/operators/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
use base64::Engine;
use serde_cbor::value::{Value, from_value};
use std::convert::TryFrom;

use crate::{
error::RadError,
hash_functions::{self, RadonHashFunctions},
types::{RadonType, bytes::RadonBytes, string::RadonString},
types::{
RadonType,
bytes::{RadonBytes, RadonBytesEncoding},
integer::RadonInteger,
string::RadonString,
},
};

pub fn to_string(input: &RadonBytes) -> Result<RadonString, RadError> {
RadonString::try_from(Value::Text(hex::encode(input.value())))
pub fn as_integer(input: &RadonBytes) -> Result<RadonInteger, RadError> {
let input_value_len = input.value().len();
match input_value_len {
1..=16 => {
let mut bytes_array = [0u8; 16];
bytes_array[16 - input_value_len..].copy_from_slice(&input.value());
Ok(RadonInteger::from(i128::from_be_bytes(bytes_array)))
}
17.. => Err(RadError::ParseInt {
message: "Input buffer too big".to_string(),
}),
_ => Err(RadError::EmptyArray),
}
}

pub fn hash(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError> {
Expand All @@ -27,14 +44,70 @@ pub fn hash(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError>

Ok(RadonBytes::from(digest))
}

pub fn length(input: &RadonBytes) -> RadonInteger {
RadonInteger::from(input.value().len() as i128)
}

pub fn slice(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonString::radon_type_name(),
operator: "BytesSlice".to_string(),
args: args.to_vec(),
};
let end_index = input.value().len();
if end_index > 0 {
let start_index = from_value::<i64>(args[0].clone())
.unwrap_or_default()
.rem_euclid(end_index as i64) as usize;
let mut slice = input.value().as_slice().split_at(start_index).1.to_vec();
if args.len() == 2 {
let end_index = from_value::<i64>(args[1].clone())
.unwrap_or_default()
.rem_euclid(end_index as i64) as usize;
slice.truncate(end_index - start_index);
}
Ok(RadonBytes::from(slice))
} else {
Err(wrong_args())
}
}

pub fn to_string(input: &RadonBytes, args: &Option<Vec<Value>>) -> Result<RadonString, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonString::radon_type_name(),
operator: "Stringify".to_string(),
args: args.to_owned().unwrap_or_default().to_vec(),
};
let mut bytes_encoding = RadonBytesEncoding::Hex;
if let Some(args) = args {
if !args.is_empty() {
let arg = args.first().ok_or_else(wrong_args)?.to_owned();
let bytes_encoding_u8 = from_value::<u8>(arg).map_err(|_| wrong_args())?;
bytes_encoding =
RadonBytesEncoding::try_from(bytes_encoding_u8).map_err(|_| wrong_args())?;
}
}
match bytes_encoding {
RadonBytesEncoding::Hex => RadonString::try_from(Value::Text(hex::encode(input.value()))),
RadonBytesEncoding::Base64 => RadonString::try_from(Value::Text(
base64::engine::general_purpose::STANDARD.encode(input.value()),
)),
RadonBytesEncoding::Utf8 => Ok(RadonString::from(
String::from_utf8(input.value().to_vec()).unwrap_or_default(),
)),
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bytes_to_string() {
let input = RadonBytes::from(vec![0x01, 0x02, 0x03]);
let output = to_string(&input).unwrap().value();
let valid_args = Some(vec![Value::from(0x00)]);
let output = to_string(&input, &valid_args).unwrap().value();

let valid_expected = "010203".to_string();

Expand Down
6 changes: 5 additions & 1 deletion rad/src/operators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ pub enum RadonOpCodes {
BooleanNegate = 0x22,
///////////////////////////////////////////////////////////////////////
// Bytes operator codes (start at 0x30)
BytesAsString = 0x30,
BytesToString = 0x30,
BytesHash = 0x31,
BytesAsInteger = 0x32,
BytesLength = 0x34,
BytesSlice = 0x3C,

///////////////////////////////////////////////////////////////////////
// Integer operator codes (start at 0x40)
IntegerAbsolute = 0x40,
Expand Down
23 changes: 21 additions & 2 deletions rad/src/types/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::{
script::RadonCall,
types::{RadonType, RadonTypes},
};
use num_enum::TryFromPrimitive;
use serde::Serialize;
use serde_cbor::value::Value;
use std::{
convert::{TryFrom, TryInto},
Expand All @@ -13,6 +15,16 @@ use witnet_data_structures::radon_report::ReportContext;

const RADON_BYTES_TYPE_NAME: &str = "RadonBytes";

/// List of support string-encoding algorithms for buffers
#[derive(Debug, Default, PartialEq, Eq, Serialize, TryFromPrimitive)]
#[repr(u8)]
pub enum RadonBytesEncoding {
#[default]
Hex = 0,
Base64 = 1,
Utf8 = 2,
}

#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct RadonBytes {
value: Vec<u8>,
Expand Down Expand Up @@ -84,12 +96,19 @@ impl Operable for RadonBytes {
match call {
// Identity
(RadonOpCodes::Identity, None) => identity(RadonTypes::from(self.clone())),
(RadonOpCodes::BytesAsString, None) => {
bytes_operators::to_string(self).map(RadonTypes::from)
(RadonOpCodes::BytesAsInteger, None) => bytes_operators::as_integer(self)
.map(RadonTypes::from),
(RadonOpCodes::BytesLength, None) => {
Ok(RadonTypes::from(bytes_operators::length(self)))
}
(RadonOpCodes::BytesHash, Some(args)) => {
bytes_operators::hash(self, args.as_slice()).map(RadonTypes::from)
}
(RadonOpCodes::BytesSlice, Some(args)) => bytes_operators::slice(self, args.as_slice())
.map(RadonTypes::from),
(RadonOpCodes::BytesToString, args) => bytes_operators::to_string(self, args)
.map(RadonTypes::from),

// Unsupported / unimplemented
(op_code, args) => Err(RadError::UnsupportedOperator {
input_type: RADON_BYTES_TYPE_NAME.to_string(),
Expand Down
Loading