diff --git a/Cargo.lock b/Cargo.lock index b294ff5a..a0a70338 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1380,6 +1380,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2657,6 +2668,9 @@ dependencies = [ "indexmap", "itertools", "k256", + "num-bigint", + "num-integer", + "num-traits", "pallas-addresses", "pallas-codec", "pallas-crypto", diff --git a/crates/aiken-lang/src/builder.rs b/crates/aiken-lang/src/builder.rs index 3f6f6075..c6dfb7c3 100644 --- a/crates/aiken-lang/src/builder.rs +++ b/crates/aiken-lang/src/builder.rs @@ -8,8 +8,11 @@ use uplc::{ Constant as UplcConstant, Name, Term, Type as UplcType, }, builtins::DefaultFunction, - machine::runtime::{convert_constr_to_tag, ANY_TAG}, - BigInt, Constr, KeyValuePairs, PlutusData, + machine::{ + runtime::{convert_constr_to_tag, ANY_TAG}, + to_pallas_bigint, + }, + Constr, KeyValuePairs, PlutusData, }; use crate::{ @@ -332,7 +335,7 @@ pub fn convert_data_to_type(term: Term, field_type: &Arc) -> Term>) -> Vec { - UplcConstant::Data(PlutusData::BigInt(BigInt::Int((*i).try_into().unwrap()))) - } + UplcConstant::Integer(i) => UplcConstant::Data(PlutusData::BigInt(to_pallas_bigint(i))), UplcConstant::ByteString(b) => { UplcConstant::Data(PlutusData::BoundedBytes(b.clone().try_into().unwrap())) } diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index a2679763..9c035e33 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -4417,7 +4417,7 @@ impl<'a> CodeGenerator<'a> { apply_wrap( apply_wrap( DefaultFunction::EqualsInteger.into(), - Term::Constant(UplcConstant::Integer(constr_index as i128).into()), + Term::Constant(UplcConstant::Integer(constr_index.into()).into()), ), constr_index_exposer(constr), ), @@ -4975,7 +4975,7 @@ impl<'a> CodeGenerator<'a> { term = apply_wrap( apply_wrap( DefaultFunction::ConstrData.into(), - Term::Constant(UplcConstant::Integer(constr_index as i128).into()), + Term::Constant(UplcConstant::Integer(constr_index.into()).into()), ), term, ); @@ -5234,7 +5234,7 @@ impl<'a> CodeGenerator<'a> { term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::ConstrData), - Term::Constant(UplcConstant::Integer(0).into()), + Term::Constant(UplcConstant::Integer(0.into()).into()), ), term, ); @@ -5342,7 +5342,7 @@ impl<'a> CodeGenerator<'a> { UnOp::Negate => apply_wrap( apply_wrap( DefaultFunction::SubtractInteger.into(), - Term::Constant(UplcConstant::Integer(0).into()), + Term::Constant(UplcConstant::Integer(0.into()).into()), ), value, ), @@ -5391,7 +5391,7 @@ impl<'a> CodeGenerator<'a> { ), term, ), - Term::Constant(UplcConstant::Integer(tuple_index as i128).into()), + Term::Constant(UplcConstant::Integer(tuple_index.into()).into()), ), &tipo.get_inner_types()[tuple_index], ); diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 4747839a..8fd4c335 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -33,6 +33,9 @@ itertools = "0.10.5" indexmap = "1.9.2" secp256k1 = { version = "0.26.0", optional = true } k256 = { version = "0.12.0", optional = true } +num-bigint = "0.4.3" +num-traits = "0.2.15" +num-integer = "0.1.45" [dev-dependencies] hex = "0.4.3" diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index f3b44189..118d8cbf 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -4,6 +4,7 @@ use std::{ rc::Rc, }; +use num_bigint::BigInt; use serde::{ self, de::{self, Deserialize, Deserializer, MapAccess, Visitor}, @@ -228,7 +229,7 @@ where #[derive(Debug, Clone, PartialEq)] pub enum Constant { // tag: 0 - Integer(i128), + Integer(BigInt), // tag: 1 ByteString(Vec), // tag: 2 diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index a7e5484b..65a74716 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -391,6 +391,9 @@ impl Encode for Constant { match self { Constant::Integer(i) => { encode_constant(&[0], e)?; + + let i: i128 = i.try_into().unwrap(); + i.encode(e)?; } @@ -444,7 +447,11 @@ impl Encode for Constant { fn encode_constant_value(x: &Constant, e: &mut Encoder) -> Result<(), en::Error> { match x { - Constant::Integer(x) => x.encode(e), + Constant::Integer(x) => { + let x: i128 = x.try_into().unwrap(); + + x.encode(e) + } Constant::ByteString(b) => b.encode(e), Constant::String(s) => s.encode(e), Constant::Unit => Ok(()), @@ -491,7 +498,7 @@ fn encode_type(typ: &Type, bytes: &mut Vec) { impl<'b> Decode<'b> for Constant { fn decode(d: &mut Decoder) -> Result { match &decode_constant(d)?[..] { - [0] => Ok(Constant::Integer(i128::decode(d)?)), + [0] => Ok(Constant::Integer(i128::decode(d)?.into())), [1] => Ok(Constant::ByteString(Vec::::decode(d)?)), [2] => Ok(Constant::String(String::decode(d)?)), [3] => Ok(Constant::Unit), @@ -534,7 +541,7 @@ impl<'b> Decode<'b> for Constant { fn decode_constant_value(typ: Rc, d: &mut Decoder) -> Result { match typ.as_ref() { - Type::Integer => Ok(Constant::Integer(i128::decode(d)?)), + Type::Integer => Ok(Constant::Integer(i128::decode(d)?.into())), Type::ByteString => Ok(Constant::ByteString(Vec::::decode(d)?)), Type::String => Ok(Constant::String(String::decode(d)?)), Type::Unit => Ok(Constant::Unit), @@ -813,7 +820,7 @@ mod test { fn flat_encode_integer() { let program = Program:: { version: (11, 22, 33), - term: Term::Constant(Constant::Integer(11).into()), + term: Term::Constant(Constant::Integer(11.into()).into()), }; let expected_bytes = vec![ @@ -833,8 +840,8 @@ mod test { Constant::ProtoList( Type::List(Type::Integer.into()), vec![ - Constant::ProtoList(Type::Integer, vec![Constant::Integer(7)]), - Constant::ProtoList(Type::Integer, vec![Constant::Integer(5)]), + Constant::ProtoList(Type::Integer, vec![Constant::Integer(7.into())]), + Constant::ProtoList(Type::Integer, vec![Constant::Integer(5.into())]), ], ) .into(), @@ -862,11 +869,11 @@ mod test { Constant::ProtoPair( Type::Integer, Type::Bool, - Constant::Integer(11).into(), + Constant::Integer(11.into()).into(), Constant::Bool(true).into(), ) .into(), - Constant::Integer(11).into(), + Constant::Integer(11.into()).into(), ) .into(), ), @@ -895,8 +902,8 @@ mod test { Constant::ProtoList( Type::List(Type::Integer.into()), vec![ - Constant::ProtoList(Type::Integer, vec![Constant::Integer(7)]), - Constant::ProtoList(Type::Integer, vec![Constant::Integer(5)]), + Constant::ProtoList(Type::Integer, vec![Constant::Integer(7.into())]), + Constant::ProtoList(Type::Integer, vec![Constant::Integer(5.into())]), ], ) .into(), @@ -924,11 +931,11 @@ mod test { Constant::ProtoPair( Type::Integer, Type::Bool, - Constant::Integer(11).into(), + Constant::Integer(11.into()).into(), Constant::Bool(true).into(), ) .into(), - Constant::Integer(11).into(), + Constant::Integer(11.into()).into(), ) .into(), ), @@ -947,7 +954,7 @@ mod test { let expected_program = Program { version: (11, 22, 33), - term: Term::Constant(Constant::Integer(11).into()), + term: Term::Constant(Constant::Integer(11.into()).into()), }; let actual_program: Program = Program::unflat(&bytes).unwrap(); diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index f45220b6..692f5d53 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -1,3 +1,4 @@ +use num_traits::sign::Signed; use std::{collections::VecDeque, ops::Deref, rc::Rc}; use crate::{ @@ -11,7 +12,8 @@ pub mod runtime; use cost_model::{ExBudget, StepKind}; pub use error::Error; -use pallas_primitives::babbage::{BigInt, Language, PlutusData}; +use num_bigint::BigInt; +use pallas_primitives::babbage::{self as pallas, Language, PlutusData}; use self::{cost_model::CostModel, runtime::BuiltinRuntime}; @@ -531,6 +533,37 @@ pub enum Value { }, } +fn integer_log2(i: BigInt) -> i64 { + let (_, bytes) = i.to_bytes_be(); + match bytes.first() { + None => unreachable!("empty number?"), + Some(u) => (8 - u.leading_zeros() - 1) as i64 + 8 * (bytes.len() - 1) as i64, + } +} + +pub fn from_pallas_bigint(n: &pallas::BigInt) -> BigInt { + match n { + pallas::BigInt::Int(i) => i128::from(*i).into(), + pallas::BigInt::BigUInt(bytes) => BigInt::from_bytes_be(num_bigint::Sign::Plus, bytes), + pallas::BigInt::BigNInt(bytes) => BigInt::from_bytes_be(num_bigint::Sign::Minus, bytes), + } +} + +pub fn to_pallas_bigint(n: &BigInt) -> pallas::BigInt { + if n.bits() <= 64 { + let regular_int: i64 = n.try_into().unwrap(); + let pallas_int: pallas_codec::utils::Int = regular_int.into(); + + pallas::BigInt::Int(pallas_int) + } else if n.is_positive() { + let (_, bytes) = n.to_bytes_be(); + pallas::BigInt::BigUInt(bytes.into()) + } else { + let (_, bytes) = n.to_bytes_be(); + pallas::BigInt::BigNInt(bytes.into()) + } +} + impl Value { pub fn is_integer(&self) -> bool { matches!(self, Value::Con(i) if matches!(i.as_ref(), Constant::Integer(_))) @@ -545,10 +578,10 @@ impl Value { match self { Value::Con(c) => match c.as_ref() { Constant::Integer(i) => { - if *i == 0 { + if *i == 0.into() { 1 } else { - ((i.abs() as f64).log2().floor() as i64 / 64) + 1 + (integer_log2(i.abs()) / 64) + 1 } } Constant::ByteString(b) => { @@ -607,12 +640,9 @@ impl Value { stack = new_stack; } PlutusData::BigInt(i) => { - if let BigInt::Int(g) = i { - let numb: i128 = (*g).try_into().unwrap(); - total += Value::Con(Constant::Integer(numb).into()).to_ex_mem(); - } else { - unreachable!() - }; + let i = from_pallas_bigint(i); + + total += Value::Con(Constant::Integer(i).into()).to_ex_mem(); } PlutusData::BoundedBytes(b) => { let byte_string: Vec = b.deref().clone(); @@ -730,3 +760,234 @@ impl From<&Constant> for Type { } } } + +#[cfg(test)] +mod tests { + use num_bigint::BigInt; + + use super::{cost_model::ExBudget, integer_log2, Value}; + use crate::{ + ast::{Constant, NamedDeBruijn, Program, Term}, + builtins::DefaultFunction, + }; + + #[test] + fn add_big_ints() { + let program: Program = Program { + version: (0, 0, 0), + term: Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::AddInteger).into(), + argument: Term::Constant(Constant::Integer(i128::MAX.into()).into()).into(), + } + .into(), + argument: Term::Constant(Constant::Integer(i128::MAX.into()).into()).into(), + }, + }; + + let (eval_result, _, _) = program.eval(ExBudget::default()); + + let term = eval_result.unwrap(); + + assert_eq!( + term, + Term::Constant( + Constant::Integer( + Into::::into(i128::MAX) + Into::::into(i128::MAX) + ) + .into() + ) + ); + } + + #[test] + fn divide_integer() { + let make_program = |fun: DefaultFunction, n: i32, m: i32| Program:: { + version: (0, 0, 0), + term: Term::Apply { + function: Term::Apply { + function: Term::Builtin(fun).into(), + argument: Term::Constant(Constant::Integer(n.into()).into()).into(), + } + .into(), + argument: Term::Constant(Constant::Integer(m.into()).into()).into(), + }, + }; + + let test_data = vec![ + (DefaultFunction::DivideInteger, 8, 3, 2), + (DefaultFunction::DivideInteger, 8, -3, -3), + (DefaultFunction::DivideInteger, -8, 3, -3), + (DefaultFunction::DivideInteger, -8, -3, 2), + (DefaultFunction::QuotientInteger, 8, 3, 2), + (DefaultFunction::QuotientInteger, 8, -3, -2), + (DefaultFunction::QuotientInteger, -8, 3, -2), + (DefaultFunction::QuotientInteger, -8, -3, 2), + (DefaultFunction::RemainderInteger, 8, 3, 2), + (DefaultFunction::RemainderInteger, 8, -3, 2), + (DefaultFunction::RemainderInteger, -8, 3, -2), + (DefaultFunction::RemainderInteger, -8, -3, -2), + (DefaultFunction::ModInteger, 8, 3, 2), + (DefaultFunction::ModInteger, 8, -3, -1), + (DefaultFunction::ModInteger, -8, 3, 1), + (DefaultFunction::ModInteger, -8, -3, -2), + ]; + + for (fun, n, m, result) in test_data { + let (eval_result, _, _) = make_program(fun, n, m).eval(ExBudget::default()); + + assert_eq!( + eval_result.unwrap(), + Term::Constant(Constant::Integer(result.into()).into()) + ); + } + } + + #[test] + fn to_ex_mem_bigint() { + let value = Value::Con(Constant::Integer(1.into()).into()); + + assert_eq!(value.to_ex_mem(), 1); + + let value = Value::Con(Constant::Integer(42.into()).into()); + + assert_eq!(value.to_ex_mem(), 1); + + let value = Value::Con( + Constant::Integer(BigInt::parse_bytes("18446744073709551615".as_bytes(), 10).unwrap()) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 1); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("999999999999999999999999999999".as_bytes(), 10).unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 2); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("170141183460469231731687303715884105726".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 2); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("170141183460469231731687303715884105727".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 2); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("170141183460469231731687303715884105728".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 2); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("170141183460469231731687303715884105729".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 2); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("340282366920938463463374607431768211458".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 3); + + let value = Value::Con( + Constant::Integer( + BigInt::parse_bytes("999999999999999999999999999999999999999999".as_bytes(), 10) + .unwrap(), + ) + .into(), + ); + + assert_eq!(value.to_ex_mem(), 3); + + let value = + Value::Con(Constant::Integer(BigInt::parse_bytes("999999999999999999999999999999999999999999999999999999999999999999999999999999999999".as_bytes(), 10).unwrap()).into()); + + assert_eq!(value.to_ex_mem(), 5); + } + + #[test] + fn integer_log2_oracle() { + // Values come from the Haskell implementation + assert_eq!(integer_log2(1.into()), 0); + assert_eq!(integer_log2(42.into()), 5); + assert_eq!( + integer_log2(BigInt::parse_bytes("18446744073709551615".as_bytes(), 10).unwrap()), + 63 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("999999999999999999999999999999".as_bytes(), 10).unwrap() + ), + 99 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("170141183460469231731687303715884105726".as_bytes(), 10) + .unwrap() + ), + 126 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("170141183460469231731687303715884105727".as_bytes(), 10) + .unwrap() + ), + 126 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("170141183460469231731687303715884105728".as_bytes(), 10) + .unwrap() + ), + 127 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("340282366920938463463374607431768211458".as_bytes(), 10) + .unwrap() + ), + 128 + ); + assert_eq!( + integer_log2( + BigInt::parse_bytes("999999999999999999999999999999999999999999".as_bytes(), 10) + .unwrap() + ), + 139 + ); + assert_eq!( + integer_log2(BigInt::parse_bytes("999999999999999999999999999999999999999999999999999999999999999999999999999999999999".as_bytes(), 10).unwrap()), + 279 + ); + } +} diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 00e20cbf..f480b6ab 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -1,5 +1,7 @@ use std::string::FromUtf8Error; +use num_bigint::BigInt; + use crate::ast::{NamedDeBruijn, Term, Type}; use super::{ExBudget, Value}; @@ -37,9 +39,9 @@ pub enum Error { #[error("Decoding utf8")] Utf8(#[from] FromUtf8Error), #[error("Out of Bounds\n\nindex: {}\nbytestring: {}\npossible: 0 - {}", .0, hex::encode(.1), .1.len() - 1)] - ByteStringOutOfBounds(i128, Vec), + ByteStringOutOfBounds(BigInt, Vec), #[error("Divide By Zero\n\n{0} / {1}")] - DivideByZero(i128, i128), + DivideByZero(BigInt, BigInt), #[error("Ed25519S PublicKey should be 32 bytes but it was {0}")] UnexpectedEd25519PublicKeyLength(usize), #[error("Ed25519S Signature should be 64 bytes but it was {0}")] diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index 2027a75a..db2f0053 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,6 +1,7 @@ use std::{ops::Deref, rc::Rc}; -use pallas_primitives::babbage::{BigInt, Constr, PlutusData}; +use num_integer::Integer; +use pallas_primitives::babbage::{Constr, PlutusData}; use crate::{ ast::{Constant, Type}, @@ -10,7 +11,7 @@ use crate::{ use super::{ cost_model::{BuiltinCosts, ExBudget}, - Error, Value, + from_pallas_bigint, to_pallas_bigint, Error, Value, }; //#[derive(std::cmp::PartialEq)] @@ -330,10 +331,9 @@ impl DefaultFunction { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - match arg1.checked_add(*arg2) { - Some(res) => Ok(Value::Con(Constant::Integer(res).into()).into()), - None => Err(Error::OverflowError), - } + let result = arg1 + arg2; + + Ok(Value::Con(Constant::Integer(result).into()).into()) } _ => unreachable!(), } @@ -344,10 +344,9 @@ impl DefaultFunction { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - match arg1.checked_sub(*arg2) { - Some(res) => Ok(Value::Con(Constant::Integer(res).into()).into()), - None => Err(Error::OverflowError), - } + let result = arg1 - arg2; + + Ok(Value::Con(Constant::Integer(result).into()).into()) } _ => unreachable!(), } @@ -358,9 +357,25 @@ impl DefaultFunction { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - match arg1.checked_mul(*arg2) { - Some(res) => Ok(Value::Con(Constant::Integer(res).into()).into()), - None => Err(Error::OverflowError), + let result = arg1 * arg2; + + Ok(Value::Con(Constant::Integer(result).into()).into()) + } + _ => unreachable!(), + } + } + _ => unreachable!(), + }, + DefaultFunction::DivideInteger => match (args[0].as_ref(), args[1].as_ref()) { + (Value::Con(integer1), Value::Con(integer2)) => { + match (integer1.as_ref(), integer2.as_ref()) { + (Constant::Integer(arg1), Constant::Integer(arg2)) => { + if *arg2 != 0.into() { + let (result, _) = arg1.div_mod_floor(arg2); + + Ok(Value::Con(Constant::Integer(result).into()).into()) + } else { + Err(Error::DivideByZero(arg1.clone(), arg2.clone())) } } _ => unreachable!(), @@ -368,38 +383,16 @@ impl DefaultFunction { } _ => unreachable!(), }, - DefaultFunction::DivideInteger => { - match (args[0].as_ref(), args[1].as_ref()) { - (Value::Con(integer1), Value::Con(integer2)) => { - match (integer1.as_ref(), integer2.as_ref()) { - (Constant::Integer(arg1), Constant::Integer(arg2)) => { - if *arg2 != 0 { - let ret = (*arg1 as f64) / (*arg2 as f64); - - Ok(Value::Con(Constant::Integer(ret.floor() as i128).into()) - .into()) - } else { - Err(Error::DivideByZero(*arg1, *arg2)) - } - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } DefaultFunction::QuotientInteger => match (args[0].as_ref(), args[1].as_ref()) { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - if *arg2 != 0 { - let ret = (*arg1 as f64) / (*arg2 as f64); + if *arg2 != 0.into() { + let (result, _) = arg1.div_rem(arg2); - let ret = if ret < 0. { ret.ceil() } else { ret.floor() }; - - Ok(Value::Con(Constant::Integer(ret as i128).into()).into()) + Ok(Value::Con(Constant::Integer(result).into()).into()) } else { - Err(Error::DivideByZero(*arg1, *arg2)) + Err(Error::DivideByZero(arg1.clone(), arg2.clone())) } } _ => unreachable!(), @@ -411,12 +404,12 @@ impl DefaultFunction { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - if *arg2 != 0 { - let ret = arg1 % arg2; + if *arg2 != 0.into() { + let (_, result) = arg1.div_rem(arg2); - Ok(Value::Con(Constant::Integer(ret).into()).into()) + Ok(Value::Con(Constant::Integer(result).into()).into()) } else { - Err(Error::DivideByZero(*arg1, *arg2)) + Err(Error::DivideByZero(arg1.clone(), arg2.clone())) } } _ => unreachable!(), @@ -428,12 +421,12 @@ impl DefaultFunction { (Value::Con(integer1), Value::Con(integer2)) => { match (integer1.as_ref(), integer2.as_ref()) { (Constant::Integer(arg1), Constant::Integer(arg2)) => { - if *arg2 != 0 { - let ret = arg1 % arg2; + if *arg2 != 0.into() { + let (_, result) = arg1.div_mod_floor(arg2); - Ok(Value::Con(Constant::Integer(ret.abs()).into()).into()) + Ok(Value::Con(Constant::Integer(result).into()).into()) } else { - Err(Error::DivideByZero(*arg1, *arg2)) + Err(Error::DivideByZero(arg1.clone(), arg2.clone())) } } _ => unreachable!(), @@ -493,7 +486,12 @@ impl DefaultFunction { (Value::Con(integer), Value::Con(byte_string)) => { match (integer.as_ref(), byte_string.as_ref()) { (Constant::Integer(arg1), Constant::ByteString(arg2)) => { - let mut ret = vec![(arg1 % 256) as u8]; + let wrap = arg1.mod_floor(&256.into()); + + let byte: u8 = wrap.try_into().unwrap(); + + let mut ret = vec![byte]; + ret.extend(arg2.clone()); Ok(Value::Con(Constant::ByteString(ret).into()).into()) @@ -512,8 +510,16 @@ impl DefaultFunction { Constant::Integer(arg2), Constant::ByteString(arg3), ) => { - let skip = if 0 > *arg1 { 0 } else { *arg1 as usize }; - let take = if 0 > *arg2 { 0 } else { *arg2 as usize }; + let skip: usize = if arg1.lt(&0.into()) { + 0 + } else { + arg1.try_into().unwrap() + }; + let take: usize = if arg2.lt(&0.into()) { + 0 + } else { + arg2.try_into().unwrap() + }; let ret: Vec = arg3.iter().skip(skip).take(take).cloned().collect(); @@ -529,7 +535,7 @@ impl DefaultFunction { DefaultFunction::LengthOfByteString => match args[0].as_ref() { Value::Con(byte_string) => match byte_string.as_ref() { Constant::ByteString(arg1) => { - Ok(Value::Con(Constant::Integer(arg1.len() as i128).into()).into()) + Ok(Value::Con(Constant::Integer(arg1.len().into()).into()).into()) } _ => unreachable!(), }, @@ -539,14 +545,14 @@ impl DefaultFunction { (Value::Con(byte_string), Value::Con(integer)) => { match (byte_string.as_ref(), integer.as_ref()) { (Constant::ByteString(arg1), Constant::Integer(arg2)) => { - let index = *arg2 as usize; + let index: i128 = arg2.try_into().unwrap(); - if 0 <= *arg2 && index < arg1.len() { - let ret = arg1[index] as i128; + if 0 <= index && index < arg1.len() as i128 { + let ret = arg1[index as usize]; - Ok(Value::Con(Constant::Integer(ret).into()).into()) + Ok(Value::Con(Constant::Integer(ret.into()).into()).into()) } else { - Err(Error::ByteStringOutOfBounds(*arg2, arg1.to_vec())) + Err(Error::ByteStringOutOfBounds(arg2.clone(), arg1.to_vec())) } } _ => unreachable!(), @@ -879,10 +885,11 @@ impl DefaultFunction { }) .collect(); + let i: u64 = i.try_into().unwrap(); + let constr_data = PlutusData::Constr(Constr { - tag: convert_constr_to_tag(*i as u64).unwrap_or(ANY_TAG), - any_constructor: convert_constr_to_tag(*i as u64) - .map_or(Some(*i as u64), |_| None), + tag: convert_constr_to_tag(i).unwrap_or(ANY_TAG), + any_constructor: convert_constr_to_tag(i).map_or(Some(i), |_| None), fields: data_list, }); @@ -938,8 +945,7 @@ impl DefaultFunction { DefaultFunction::IData => match args[0].as_ref() { Value::Con(integer) => match integer.as_ref() { Constant::Integer(i) => Ok(Value::Con( - Constant::Data(PlutusData::BigInt(BigInt::Int((*i).try_into().unwrap()))) - .into(), + Constant::Data(PlutusData::BigInt(to_pallas_bigint(i))).into(), ) .into()), _ => unreachable!(), @@ -966,7 +972,7 @@ impl DefaultFunction { Constant::Integer( convert_tag_to_constr(c.tag) .unwrap_or_else(|| c.any_constructor.unwrap()) - as i128, + .into(), ) .into(), Constant::ProtoList( @@ -1048,13 +1054,7 @@ impl DefaultFunction { DefaultFunction::UnIData => match args[0].as_ref() { Value::Con(data) => match data.as_ref() { Constant::Data(PlutusData::BigInt(b)) => { - if let BigInt::Int(i) = b { - let x: i128 = (*i).try_into().unwrap(); - - Ok(Value::Con(Constant::Integer(x).into()).into()) - } else { - unreachable!() - } + Ok(Value::Con(Constant::Integer(from_pallas_bigint(b)).into()).into()) } v => Err(Error::DeserialisationError( "UnMapData".to_string(), diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 19b6815b..e14c8b73 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, str::FromStr}; +use std::{ops::Neg, rc::Rc, str::FromStr}; use crate::{ ast::{Constant, Name, Program, Term, Type}, @@ -6,6 +6,7 @@ use crate::{ }; use interner::Interner; +use num_bigint::BigInt; use pallas_primitives::{alonzo::PlutusData, Fragment}; use peg::{error::ParseError, str::LineCol}; @@ -157,8 +158,8 @@ peg::parser! { rule number() -> isize = n:$("-"* ['0'..='9']+) {? n.parse().or(Err("isize")) } - rule big_number() -> i128 - = n:$("-"* ['0'..='9']+) {? n.parse().or(Err("i128")) } + rule big_number() -> BigInt + = n:$("-"* ['0'..='9']+) {? (if n.starts_with('-') { BigInt::parse_bytes(&n.as_bytes()[1..], 10).map(|i| i.neg()) } else { BigInt::parse_bytes(n.as_bytes(), 10) }).ok_or("BigInt") } rule boolean() -> bool = b:$("True" / "False") { b == "True" } @@ -257,6 +258,8 @@ peg::parser! { #[cfg(test)] mod test { + use num_bigint::BigInt; + use crate::ast::{Constant, Name, Program, Term, Type, Unique}; use crate::builtins::DefaultFunction; use std::rc::Rc; @@ -277,7 +280,7 @@ mod test { parameter_name: x.clone().into(), body: Rc::new(Term::Var(x.into())), }), - argument: Rc::new(Term::Constant(Constant::Integer(0).into())) + argument: Rc::new(Term::Constant(Constant::Integer(0.into()).into())) } } ) @@ -344,7 +347,7 @@ mod test { super::program(uplc).unwrap(), Program:: { version: (11, 22, 33), - term: Term::Constant(Constant::Integer(11).into()), + term: Term::Constant(Constant::Integer(11.into()).into()), } ); } @@ -492,7 +495,7 @@ mod test { term: Term::Apply { function: Rc::new(Term::Apply { function: Rc::new(Term::Builtin(DefaultFunction::ConsByteString)), - argument: Rc::new(Term::Constant(Constant::Integer(256).into())), + argument: Rc::new(Term::Constant(Constant::Integer(256.into()).into())), }), argument: Rc::new(Term::Constant(Constant::ByteString(vec![]).into())) } @@ -511,9 +514,9 @@ mod test { function: Rc::new(Term::Apply { function: Rc::new(Term::Apply { function: Rc::new(Term::Builtin(DefaultFunction::SliceByteString)), - argument: Rc::new(Term::Constant(Constant::Integer(1).into())), + argument: Rc::new(Term::Constant(Constant::Integer(1.into()).into())), }), - argument: Rc::new(Term::Constant(Constant::Integer(2).into())), + argument: Rc::new(Term::Constant(Constant::Integer(2.into()).into())), }), argument: Rc::new(Term::Constant( Constant::ByteString(vec![0x00, 0xFF, 0xAA]).into() @@ -553,7 +556,10 @@ mod test { argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00]).into())) }), argument: Rc::new(Term::Constant( - Constant::Integer(9223372036854775808).into() + Constant::Integer( + BigInt::parse_bytes("9223372036854775808".as_bytes(), 10).unwrap() + ) + .into() )), } } @@ -704,9 +710,12 @@ mod test { vec![ Constant::ProtoList( Type::Integer, - vec![Constant::Integer(14), Constant::Integer(42)] + vec![Constant::Integer(14.into()), Constant::Integer(42.into())] ), - Constant::ProtoList(Type::Integer, vec![Constant::Integer(1337)]) + Constant::ProtoList( + Type::Integer, + vec![Constant::Integer(1337.into())] + ) ] ) .into() @@ -733,7 +742,7 @@ mod test { term: Term::Constant( Constant::ProtoList( Type::Integer, - vec![Constant::Integer(14), Constant::Integer(42)], + vec![Constant::Integer(14.into()), Constant::Integer(42.into())], ) .into() ) @@ -776,7 +785,7 @@ mod test { Constant::ProtoPair( Type::Integer, Type::ByteString, - Constant::Integer(14).into(), + Constant::Integer(14.into()).into(), Constant::ByteString(vec![0x42]).into(), ) .into() @@ -801,7 +810,7 @@ mod test { Constant::String(String::from("foo")).into(), Constant::ProtoList( Type::Integer, - vec![Constant::Integer(14), Constant::Integer(42)], + vec![Constant::Integer(14.into()), Constant::Integer(42.into())], ) .into() ) @@ -828,8 +837,8 @@ mod test { Constant::ProtoPair( Type::Integer, Type::Integer, - Constant::Integer(14).into(), - Constant::Integer(42).into() + Constant::Integer(14.into()).into(), + Constant::Integer(42.into()).into() ) .into() ) @@ -859,9 +868,9 @@ mod test { term: Term::Apply { function: Rc::new(Term::Apply { function: Rc::new(Term::Builtin(default_function)), - argument: Rc::new(Term::Constant(Constant::Integer(x).into())), + argument: Rc::new(Term::Constant(Constant::Integer(x.into()).into())), }), - argument: Rc::new(Term::Constant(Constant::Integer(y).into())) + argument: Rc::new(Term::Constant(Constant::Integer(y.into()).into())) } } ) diff --git a/crates/uplc/src/program_builder/constant.rs b/crates/uplc/src/program_builder/constant.rs index 27e03d3a..09257da0 100644 --- a/crates/uplc/src/program_builder/constant.rs +++ b/crates/uplc/src/program_builder/constant.rs @@ -3,7 +3,7 @@ use crate::program_builder::WithTerm; pub trait WithConstant: WithTerm { fn with_int(self, int: i128) -> Self::Next { - let term = Term::Constant(Constant::Integer(int).into()); + let term = Term::Constant(Constant::Integer(int.into()).into()); self.next(term) }