Skip to content

Commit

Permalink
feat: implement several more builtins
Browse files Browse the repository at this point in the history
  • Loading branch information
rvcas committed Oct 9, 2024
1 parent 4efdef0 commit 227dfde
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 19 deletions.
7 changes: 7 additions & 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 crates/uplc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ publish = false
blst = "0.3.13"
bumpalo = { version = "3.16.0", features = ["collections"] }
chumsky = { version = "1.0.0-alpha.7", features = ["pratt"] }
cryptoxide = "0.4.4"
minicbor = { version = "0.25.1", features = ["std"] }
rug = { version = "1.26.0", default-features = false, features = [
"integer",
Expand Down
30 changes: 30 additions & 0 deletions crates/uplc/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,34 @@ impl<'a> PlutusData<'a> {
_ => Err(MachineError::malformed_data(self)),
}
}

pub fn unwrap_map(
&'a self,
) -> Result<&'a BumpVec<(&'a PlutusData<'a>, &'a PlutusData<'a>)>, MachineError<'a>> {
match self {
PlutusData::Map(fields) => Ok(fields),
_ => Err(MachineError::malformed_data(self)),
}
}

pub fn unwrap_integer(&'a self) -> Result<&'a Integer, MachineError<'a>> {
match self {
PlutusData::Integer(i) => Ok(i),
_ => Err(MachineError::malformed_data(self)),
}
}

pub fn unwrap_byte_string(&'a self) -> Result<&'a BumpVec<u8>, MachineError<'a>> {
match self {
PlutusData::ByteString(bytes) => Ok(bytes),
_ => Err(MachineError::malformed_data(self)),
}
}

pub fn unwrap_list(&'a self) -> Result<&'a BumpVec<&'a PlutusData<'a>>, MachineError<'a>> {
match self {
PlutusData::List(items) => Ok(items),
_ => Err(MachineError::malformed_data(self)),
}
}
}
34 changes: 32 additions & 2 deletions crates/uplc/src/machine/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::array::TryFromSliceError;

use bumpalo::collections::Vec as BumpVec;

use crate::{
Expand All @@ -16,8 +18,6 @@ pub enum MachineError<'a> {
NotAConstant(&'a Value<'a>),
OpenTermEvaluated(&'a Term<'a>),
OutOfExError(ExBudget),
TypeMismatch(Type<'a>, &'a Constant<'a>),
ExpectedPair(&'a Constant<'a>),
UnexpectedBuiltinTermArgument(&'a Term<'a>),
NonPolymorphicInstantiation(&'a Value<'a>),
BuiltinTermArgumentExpected(&'a Term<'a>),
Expand All @@ -29,15 +29,37 @@ pub enum MachineError<'a> {
#[derive(Debug)]
pub enum RuntimeError<'a> {
ByteStringOutOfBounds(&'a BumpVec<'a, u8>, &'a Integer),
TypeMismatch(Type<'a>, &'a Constant<'a>),
ExpectedPair(&'a Constant<'a>),
ExpectedList(&'a Constant<'a>),
NotData(&'a Constant<'a>),
MalFormedData(&'a PlutusData<'a>),
EmptyList(&'a BumpVec<'a, &'a Constant<'a>>),
UnexpectedEd25519PublicKeyLength(TryFromSliceError),
UnexpectedEd25519SignatureLength(TryFromSliceError),
}

impl<'a> MachineError<'a> {
pub fn runtime(runtime_error: RuntimeError<'a>) -> Self {
MachineError::Runtime(runtime_error)
}

pub fn type_mismatch(expected: Type<'a>, constant: &'a Constant<'a>) -> Self {
MachineError::runtime(RuntimeError::TypeMismatch(expected, constant))
}

pub fn expected_pair(constant: &'a Constant<'a>) -> Self {
MachineError::runtime(RuntimeError::ExpectedPair(constant))
}

pub fn expected_list(constant: &'a Constant<'a>) -> Self {
MachineError::runtime(RuntimeError::ExpectedList(constant))
}

pub fn empty_list(constant: &'a BumpVec<'a, &'a Constant<'a>>) -> Self {
MachineError::runtime(RuntimeError::EmptyList(constant))
}

pub fn byte_string_out_of_bounds(byte_string: &'a BumpVec<'a, u8>, index: &'a Integer) -> Self {
MachineError::runtime(RuntimeError::ByteStringOutOfBounds(byte_string, index))
}
Expand All @@ -49,4 +71,12 @@ impl<'a> MachineError<'a> {
pub fn malformed_data(plutus_data: &'a PlutusData<'a>) -> Self {
MachineError::runtime(RuntimeError::MalFormedData(plutus_data))
}

pub fn unexpected_ed25519_public_key_length(length: TryFromSliceError) -> Self {
MachineError::runtime(RuntimeError::UnexpectedEd25519PublicKeyLength(length))
}

pub fn unexpected_ed25519_signature_length(length: TryFromSliceError) -> Self {
MachineError::runtime(RuntimeError::UnexpectedEd25519SignatureLength(length))
}
}
134 changes: 122 additions & 12 deletions crates/uplc/src/machine/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::array::TryFromSliceError;

use bumpalo::{
collections::{CollectIn, Vec as BumpVec},
Bump,
Expand All @@ -7,6 +9,7 @@ use rug::Assign;
use crate::{
builtin::DefaultFunction,
constant::{self, Constant},
data::PlutusData,
typ::Type,
};

Expand Down Expand Up @@ -238,7 +241,35 @@ impl<'a> Runtime<'a> {
DefaultFunction::Blake2b_256 => todo!(),
DefaultFunction::Keccak_256 => todo!(),
DefaultFunction::Blake2b_224 => todo!(),
DefaultFunction::VerifyEd25519Signature => todo!(),
DefaultFunction::VerifyEd25519Signature => {
use cryptoxide::ed25519;

let public_key = self.args[0].unwrap_byte_string()?;
let message = self.args[1].unwrap_byte_string()?;
let signature = self.args[2].unwrap_byte_string()?;

let public_key: [u8; 32] =
public_key
.as_slice()
.try_into()
.map_err(|e: TryFromSliceError| {
MachineError::unexpected_ed25519_public_key_length(e)
})?;

let signature: [u8; 64] =
signature
.as_slice()
.try_into()
.map_err(|e: TryFromSliceError| {
MachineError::unexpected_ed25519_signature_length(e)
})?;

let valid = ed25519::verify(message, &public_key, &signature);

let value = Value::bool(arena, valid);

Ok(value)
}
DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(),
DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(),
DefaultFunction::AppendString => todo!(),
Expand All @@ -261,22 +292,66 @@ impl<'a> Runtime<'a> {

Ok(value)
}
DefaultFunction::ChooseList => todo!(),
DefaultFunction::ChooseList => {
let (_, list) = self.args[0].unwrap_list()?;

if list.is_empty() {
Ok(self.args[1])
} else {
Ok(self.args[2])
}
}
DefaultFunction::MkCons => todo!(),
DefaultFunction::HeadList => todo!(),
DefaultFunction::TailList => todo!(),
DefaultFunction::HeadList => {
let (_, list) = self.args[0].unwrap_list()?;

if list.is_empty() {
Err(MachineError::empty_list(list))
} else {
let value = Value::con(arena, list[0]);

Ok(value)
}
}
DefaultFunction::TailList => {
let (t1, list) = self.args[0].unwrap_list()?;

if list.is_empty() {
Err(MachineError::empty_list(list))
} else {
let mut tail = BumpVec::with_capacity_in(list.len(), arena);

tail.extend_from_slice(&list[1..]);

let constant = Constant::proto_list(arena, t1, tail);

let value = Value::con(arena, constant);

Ok(value)
}
}
DefaultFunction::NullList => todo!(),
DefaultFunction::ChooseData => todo!(),
DefaultFunction::ChooseData => {
let con = self.args[0].unwrap_constant()?.unwrap_data()?;

match con {
PlutusData::Constr { .. } => Ok(self.args[1]),
PlutusData::Map(_) => Ok(self.args[2]),
PlutusData::List(_) => Ok(self.args[3]),
PlutusData::Integer(_) => Ok(self.args[4]),
PlutusData::ByteString(_) => Ok(self.args[5]),
}
}
DefaultFunction::ConstrData => todo!(),
DefaultFunction::MapData => todo!(),
DefaultFunction::ListData => todo!(),
DefaultFunction::IData => todo!(),
DefaultFunction::BData => todo!(),
DefaultFunction::UnConstrData => {
let arg1 = self.args[0].unwrap_constant()?;
let plutus_data = arg1.unwrap_data()?;

let (tag, fields) = plutus_data.unwrap_constr()?;
let (tag, fields) = self.args[0]
.unwrap_constant()?
.unwrap_data()?
.unwrap_constr()?;

let constant = Constant::proto_pair(
arena,
Expand All @@ -298,9 +373,44 @@ impl<'a> Runtime<'a> {
Ok(value)
}
DefaultFunction::UnMapData => todo!(),
DefaultFunction::UnListData => todo!(),
DefaultFunction::UnIData => todo!(),
DefaultFunction::UnBData => todo!(),
DefaultFunction::UnListData => {
let list = self.args[0]
.unwrap_constant()?
.unwrap_data()?
.unwrap_list()?;

let constant = Constant::proto_list(
arena,
Type::data(arena),
list.iter()
.map(|d| Constant::data(arena, d))
.collect_in(arena),
);

let value = Value::con(arena, constant);

Ok(value)
}
DefaultFunction::UnIData => {
let i = self.args[0]
.unwrap_constant()?
.unwrap_data()?
.unwrap_integer()?;

let value = Value::integer(arena, i);

Ok(value)
}
DefaultFunction::UnBData => {
let bs = self.args[0]
.unwrap_constant()?
.unwrap_data()?
.unwrap_byte_string()?;

let value = Value::byte_string(arena, bs.clone());

Ok(value)
}
DefaultFunction::EqualsData => todo!(),
DefaultFunction::SerialiseData => todo!(),
DefaultFunction::MkPairData => todo!(),
Expand Down
22 changes: 17 additions & 5 deletions crates/uplc/src/machine/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'a> Value<'a> {
let inner = self.unwrap_constant()?;

let Constant::Integer(integer) = inner else {
return Err(MachineError::TypeMismatch(Type::Integer, inner));
return Err(MachineError::type_mismatch(Type::Integer, inner));
};

Ok(integer)
Expand All @@ -92,7 +92,7 @@ impl<'a> Value<'a> {
let inner = self.unwrap_constant()?;

let Constant::ByteString(byte_string) = inner else {
return Err(MachineError::TypeMismatch(Type::ByteString, inner));
return Err(MachineError::type_mismatch(Type::ByteString, inner));
};

Ok(byte_string)
Expand All @@ -102,13 +102,13 @@ impl<'a> Value<'a> {
let inner = self.unwrap_constant()?;

let Constant::Boolean(b) = inner else {
return Err(MachineError::TypeMismatch(Type::Bool, inner));
return Err(MachineError::type_mismatch(Type::Bool, inner));
};

Ok(*b)
}

pub(super) fn unwrap_pair(
pub fn unwrap_pair(
&'a self,
) -> Result<
(
Expand All @@ -122,12 +122,24 @@ impl<'a> Value<'a> {
let inner = self.unwrap_constant()?;

let Constant::ProtoPair(t1, t2, first, second) = inner else {
return Err(MachineError::ExpectedPair(inner));
return Err(MachineError::expected_pair(inner));
};

Ok((t1, t2, first, second))
}

pub fn unwrap_list(
&'a self,
) -> Result<(&'a Type<'a>, &'a BumpVec<'a, &'a Constant<'a>>), MachineError<'a>> {
let inner = self.unwrap_constant()?;

let Constant::ProtoList(t1, list) = inner else {
return Err(MachineError::expected_list(inner));
};

Ok((t1, list))
}

pub fn unwrap_constant(&'a self) -> Result<&'a Constant<'a>, MachineError<'a>> {
let Value::Con(item) = self else {
return Err(MachineError::NotAConstant(self));
Expand Down

0 comments on commit 227dfde

Please sign in to comment.