feat: implement integerToByteString

Co-authored-by: Kasey White <kwhitemsg@gmail.com>
This commit is contained in:
rvcas 2024-02-16 11:50:20 -05:00 committed by Lucas
parent c7dd4d0e48
commit da6e5ec6d1
3 changed files with 94 additions and 3 deletions

View File

@ -40,7 +40,14 @@ pub enum Error {
NotAConstant(Value), NotAConstant(Value),
#[error("The evaluation never reached a final state")] #[error("The evaluation never reached a final state")]
MachineNeverReachedDone, MachineNeverReachedDone,
#[error("integerToByteString encountered negative size {0}")]
IntegerToByteStringNegativeSize(BigInt),
#[error("integerToByteString encountered negative input {0}")]
IntegerToByteStringNegativeInput(BigInt),
#[error("integerToByteString encountered size {0} which is bigger than the max size of {1}")]
IntegerToByteStringSizeTooBig(BigInt, i64),
#[error("integerToByteString encountered size {0} which is not enough space for {1} bytes")]
IntegerToByteStringSizeTooSmall(BigInt, usize),
#[error("Decoding utf8")] #[error("Decoding utf8")]
Utf8(#[from] FromUtf8Error), Utf8(#[from] FromUtf8Error),
#[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)] #[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)]

View File

@ -2,12 +2,14 @@ use std::{mem::size_of, ops::Deref, rc::Rc};
use num_bigint::BigInt; use num_bigint::BigInt;
use num_integer::Integer; use num_integer::Integer;
use num_traits::{Signed, ToBytes, Zero};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pallas::ledger::primitives::babbage::{Language, PlutusData}; use pallas::ledger::primitives::babbage::{Language, PlutusData};
use crate::{ use crate::{
ast::{Constant, Data, Type}, ast::{Constant, Data, Type},
builtins::DefaultFunction, builtins::DefaultFunction,
machine::value::integer_log2,
plutus_data_to_bytes, plutus_data_to_bytes,
}; };
@ -32,6 +34,8 @@ const BLST_P1_COMPRESSED_SIZE: usize = 48;
const BLST_P2_COMPRESSED_SIZE: usize = 96; const BLST_P2_COMPRESSED_SIZE: usize = 96;
const INTEGER_TO_BYTE_STRING_MAXIMUM_OUTPUT_LENGTH: i64 = 8192;
//#[derive(std::cmp::PartialEq)] //#[derive(std::cmp::PartialEq)]
//pub enum EvalMode { //pub enum EvalMode {
// Immediate, // Immediate,
@ -1298,6 +1302,80 @@ impl DefaultFunction {
Ok(Value::Con(constant.into())) Ok(Value::Con(constant.into()))
} }
DefaultFunction::IntegerToByteString => {
let endianness = args[0].unwrap_bool()?;
let size = args[1].unwrap_integer()?;
let input = args[2].unwrap_integer()?;
if size.is_negative() {
return Err(Error::IntegerToByteStringNegativeSize(size.clone()));
}
if size > &INTEGER_TO_BYTE_STRING_MAXIMUM_OUTPUT_LENGTH.into() {
return Err(Error::IntegerToByteStringSizeTooBig(
size.clone(),
INTEGER_TO_BYTE_STRING_MAXIMUM_OUTPUT_LENGTH,
));
}
if size.is_zero()
&& integer_log2(input.clone())
>= 8 * INTEGER_TO_BYTE_STRING_MAXIMUM_OUTPUT_LENGTH
{
let required = integer_log2(input.clone()) / 8 + 1;
return Err(Error::IntegerToByteStringSizeTooBig(
required.into(),
INTEGER_TO_BYTE_STRING_MAXIMUM_OUTPUT_LENGTH,
));
}
if input.is_negative() {
return Err(Error::IntegerToByteStringNegativeInput(input.clone()));
}
let size_unwrapped: usize = size.try_into().unwrap();
if input.is_zero() {
let constant = Constant::ByteString(vec![0; size_unwrapped]);
return Ok(Value::Con(constant.into()));
}
let mut bytes = if *endianness {
input.to_be_bytes()
} else {
input.to_le_bytes()
};
if !size.is_zero() && bytes.len() > size_unwrapped {
return Err(Error::IntegerToByteStringSizeTooSmall(
size.clone(),
bytes.len(),
));
}
if size_unwrapped > 0 {
let padding_size = size_unwrapped - bytes.len();
let mut padding = vec![0; padding_size];
if *endianness {
padding.append(&mut bytes);
bytes = padding;
} else {
bytes.append(&mut padding);
}
};
let constant = Constant::ByteString(bytes);
Ok(Value::Con(constant.into()))
}
DefaultFunction::ByteStringToInteger => {
todo!("do it not live")
}
} }
} }
} }

View File

@ -1,7 +1,7 @@
use std::{collections::VecDeque, mem::size_of, ops::Deref, rc::Rc}; use std::{collections::VecDeque, mem::size_of, ops::Deref, rc::Rc};
use num_bigint::BigInt; use num_bigint::BigInt;
use num_traits::{Signed, ToPrimitive}; use num_traits::{Signed, ToPrimitive, Zero};
use pallas::ledger::primitives::babbage::{self, PlutusData}; use pallas::ledger::primitives::babbage::{self, PlutusData};
use crate::{ use crate::{
@ -386,8 +386,13 @@ impl TryFrom<&Value> for Constant {
} }
} }
fn integer_log2(i: BigInt) -> i64 { pub fn integer_log2(i: BigInt) -> i64 {
if i.is_zero() {
return 0;
}
let (_, bytes) = i.to_bytes_be(); let (_, bytes) = i.to_bytes_be();
match bytes.first() { match bytes.first() {
None => unreachable!("empty number?"), None => unreachable!("empty number?"),
Some(u) => (8 - u.leading_zeros() - 1) as i64 + 8 * (bytes.len() - 1) as i64, Some(u) => (8 - u.leading_zeros() - 1) as i64 + 8 * (bytes.len() - 1) as i64,
@ -524,6 +529,7 @@ mod tests {
#[test] #[test]
fn integer_log2_oracle() { fn integer_log2_oracle() {
// Values come from the Haskell implementation // Values come from the Haskell implementation
assert_eq!(integer_log2(0.into()), 0);
assert_eq!(integer_log2(1.into()), 0); assert_eq!(integer_log2(1.into()), 0);
assert_eq!(integer_log2(42.into()), 5); assert_eq!(integer_log2(42.into()), 5);
assert_eq!( assert_eq!(