Implement quick builder on PlutusData.

In the same spirit of the existing Term builder; I also added a `data`
  method to lift a `PlutusData` into a `Term<T>` and generalized a bit
  the builder to only require a `Term<Name>` when necessary and remain
  generic otherwise.

  The `PlutusData` builder could potentially be upstreamed to pallas
  diretly.
This commit is contained in:
KtorZ 2023-04-05 15:32:39 +02:00
parent f311e048b7
commit 9033b44044
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 135 additions and 75 deletions

View File

@ -1,23 +1,3 @@
use std::{
fmt::{self, Display},
hash::{self, Hash},
rc::Rc,
};
use num_bigint::BigInt;
use serde::{
self,
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
ser::{Serialize, SerializeStruct, Serializer},
};
use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart};
use pallas_primitives::{
alonzo::PlutusData,
babbage::{self as cardano, Language},
};
use pallas_traverse::ComputeHash;
use crate::{
builtins::DefaultFunction,
debruijn::{self, Converter},
@ -28,6 +8,24 @@ use crate::{
Machine,
},
};
use num_bigint::BigInt;
use num_traits::ToPrimitive;
use pallas_addresses::{Network, ShelleyAddress, ShelleyDelegationPart, ShelleyPaymentPart};
use pallas_primitives::{
alonzo::{self as pallas, Constr, PlutusData},
babbage::{self as cardano, Language},
};
use pallas_traverse::ComputeHash;
use serde::{
self,
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
ser::{Serialize, SerializeStruct, Serializer},
};
use std::{
fmt::{self, Display},
hash::{self, Hash},
rc::Rc,
};
/// This represents a program in Untyped Plutus Core.
/// A program contains a version tuple and a term.
@ -239,6 +237,61 @@ pub enum Constant {
Data(PlutusData),
}
pub struct Data {}
// TODO: See about moving these builders upstream to Pallas?
impl Data {
pub fn integer(i: BigInt) -> PlutusData {
match i.to_i64() {
Some(i) => PlutusData::BigInt(pallas::BigInt::Int(i.into())),
None => {
let (sign, bytes) = i.to_bytes_be();
match sign {
num_bigint::Sign::Minus => {
PlutusData::BigInt(pallas::BigInt::BigNInt(bytes.into()))
}
_ => PlutusData::BigInt(pallas::BigInt::BigUInt(bytes.into())),
}
}
}
}
pub fn bytestring(bytes: Vec<u8>) -> PlutusData {
PlutusData::BoundedBytes(bytes.into())
}
pub fn map(kvs: Vec<(PlutusData, PlutusData)>) -> PlutusData {
PlutusData::Map(kvs.into())
}
pub fn list(xs: Vec<PlutusData>) -> PlutusData {
PlutusData::Array(xs)
}
pub fn constr(ix: u64, fields: Vec<PlutusData>) -> PlutusData {
// NOTE: see https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L139-L155
if ix < 7 {
PlutusData::Constr(Constr {
tag: 121 + ix,
any_constructor: None,
fields,
})
} else if ix < 128 {
PlutusData::Constr(Constr {
tag: 1280 + ix - 7,
any_constructor: None,
fields,
})
} else {
PlutusData::Constr(Constr {
tag: 102,
any_constructor: Some(ix),
fields,
})
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Bool,

View File

@ -2,13 +2,14 @@ use crate::{
ast::{Constant, Name, Term, Type},
builtins::DefaultFunction,
};
use pallas_primitives::alonzo::PlutusData;
pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer";
pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer";
pub const CONSTR_GET_FIELD: &str = "__constr_get_field";
pub const EXPECT_ON_LIST: &str = "__expect_on_list";
impl Term<Name> {
impl<T> Term<T> {
pub fn apply(self, arg: Self) -> Self {
Term::Apply {
function: self.into(),
@ -16,13 +17,6 @@ impl Term<Name> {
}
}
pub fn lambda(self, parameter_name: impl ToString) -> Self {
Term::Lambda {
parameter_name: Name::text(parameter_name).into(),
body: self.into(),
}
}
pub fn force(self) -> Self {
Term::Force(self.into())
}
@ -31,10 +25,6 @@ impl Term<Name> {
Term::Delay(self.into())
}
pub fn var(name: impl ToString) -> Self {
Term::Var(Name::text(name).into())
}
pub fn integer(i: num_bigint::BigInt) -> Self {
Term::Constant(Constant::Integer(i).into())
}
@ -55,6 +45,10 @@ impl Term<Name> {
Term::Constant(Constant::Unit.into())
}
pub fn data(d: PlutusData) -> Self {
Term::Constant(Constant::Data(d).into())
}
pub fn empty_list() -> Self {
Term::Constant(Constant::ProtoList(Type::Data, vec![]).into())
}
@ -204,7 +198,7 @@ impl Term<Name> {
.force()
}
pub fn trace(self, msg_term: Term<Name>) -> Self {
pub fn trace(self, msg_term: Self) -> Self {
Term::Builtin(DefaultFunction::Trace)
.force()
.apply(msg_term)
@ -212,41 +206,34 @@ impl Term<Name> {
.force()
}
pub fn assert_on_list(self) -> Term<Name> {
self.lambda(EXPECT_ON_LIST.to_string())
.apply(
Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())),
)
.lambda(EXPECT_ON_LIST.to_string())
.apply(
Term::var("__list_to_check".to_string())
.delayed_choose_list(
Term::unit(),
Term::var("__check_with".to_string())
.apply(
Term::head_list().apply(Term::var("__list_to_check".to_string())),
)
.choose_unit(
Term::var(EXPECT_ON_LIST.to_string())
.apply(Term::var(EXPECT_ON_LIST.to_string()))
.apply(
Term::tail_list()
.apply(Term::var("__list_to_check".to_string())),
)
.apply(Term::var("__check_with".to_string())),
),
)
.lambda("__check_with".to_string())
.lambda("__list_to_check".to_string())
.lambda(EXPECT_ON_LIST),
)
}
pub fn final_wrapper(self: Term<Name>) -> Term<Name> {
pub fn final_wrapper(self) -> Self {
self.delayed_if_else(Term::unit(), Term::Error)
}
pub fn constr_fields_exposer(self: Term<Name>) -> Term<Name> {
pub fn repeat_tail_list(self, repeat: usize) -> Self {
let mut term = self;
for _ in 0..repeat {
term = Term::tail_list().apply(term);
}
term
}
}
impl Term<Name> {
pub fn lambda(self, parameter_name: impl ToString) -> Self {
Term::Lambda {
parameter_name: Name::text(parameter_name).into(),
body: self.into(),
}
}
pub fn var(name: impl ToString) -> Self {
Term::Var(Name::text(name).into())
}
pub fn constr_fields_exposer(self) -> Self {
self.lambda(CONSTR_FIELDS_EXPOSER.to_string()).apply(
Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string())))
@ -254,7 +241,7 @@ impl Term<Name> {
)
}
pub fn constr_index_exposer(self: Term<Name>) -> Term<Name> {
pub fn constr_index_exposer(self) -> Self {
self.lambda(CONSTR_INDEX_EXPOSER.to_string()).apply(
Term::fst_pair()
.apply(Term::unconstr_data().apply(Term::var("__constr_var".to_string())))
@ -262,7 +249,7 @@ impl Term<Name> {
)
}
pub fn constr_get_field(self: Term<Name>) -> Term<Name> {
pub fn constr_get_field(self) -> Self {
self.lambda(CONSTR_GET_FIELD.to_string())
.apply(
Term::var(CONSTR_GET_FIELD.to_string())
@ -298,13 +285,33 @@ impl Term<Name> {
)
}
pub fn repeat_tail_list(self: Term<Name>, repeat: usize) -> Term<Name> {
let mut term = self;
for _ in 0..repeat {
term = Term::tail_list().apply(term);
}
term
pub fn assert_on_list(self) -> Self {
self.lambda(EXPECT_ON_LIST.to_string())
.apply(
Term::var(EXPECT_ON_LIST.to_string()).apply(Term::var(EXPECT_ON_LIST.to_string())),
)
.lambda(EXPECT_ON_LIST.to_string())
.apply(
Term::var("__list_to_check".to_string())
.delayed_choose_list(
Term::unit(),
Term::var("__check_with".to_string())
.apply(
Term::head_list().apply(Term::var("__list_to_check".to_string())),
)
.choose_unit(
Term::var(EXPECT_ON_LIST.to_string())
.apply(Term::var(EXPECT_ON_LIST.to_string()))
.apply(
Term::tail_list()
.apply(Term::var("__list_to_check".to_string())),
)
.apply(Term::var("__check_with".to_string())),
),
)
.lambda("__check_with".to_string())
.lambda("__list_to_check".to_string())
.lambda(EXPECT_ON_LIST),
)
}
}