feat(bls): aiken level g1 and g2 literals

This commit is contained in:
rvcas 2023-11-06 22:58:21 -05:00 committed by Lucas
parent 90aea6476a
commit 3675762c3e
10 changed files with 397 additions and 47 deletions

1
Cargo.lock generated vendored
View File

@ -82,6 +82,7 @@ dependencies = [
name = "aiken-lang"
version = "1.0.20-alpha"
dependencies = [
"blst",
"chumsky",
"hex",
"indexmap",

View File

@ -27,6 +27,7 @@ vec1 = "1.10.1"
uplc = { path = '../uplc', version = "1.0.20-alpha" }
num-bigint = "0.4.3"
petgraph = "0.6.3"
blst = "0.3.11"
[target.'cfg(not(target_family="wasm"))'.dependencies]
chumsky = "0.9.2"

View File

@ -11,6 +11,7 @@ use std::{
ops::Range,
rc::Rc,
};
use uplc::machine::runtime::Compressable;
use vec1::Vec1;
pub const CAPTURE_VARIABLE: &str = "_capture";
@ -492,6 +493,12 @@ pub enum Constant {
bytes: Vec<u8>,
preferred_format: ByteArrayFormatPreference,
},
CurvePoint {
location: Span,
point: Curve,
preferred_format: ByteArrayFormatPreference,
},
}
impl Constant {
@ -500,6 +507,10 @@ impl Constant {
Constant::Int { .. } => builtins::int(),
Constant::String { .. } => builtins::string(),
Constant::ByteArray { .. } => builtins::byte_array(),
Constant::CurvePoint { point, .. } => match point {
Curve::Bls12_381(Bls12_381Point::G1(_)) => builtins::g1_element(),
Curve::Bls12_381(Bls12_381Point::G2(_)) => builtins::g2_element(),
},
}
}
@ -507,7 +518,8 @@ impl Constant {
match self {
Constant::Int { location, .. }
| Constant::String { location, .. }
| Constant::ByteArray { location, .. } => *location,
| Constant::ByteArray { location, .. }
| Constant::CurvePoint { location, .. } => *location,
}
}
}
@ -997,6 +1009,78 @@ pub enum ByteArrayFormatPreference {
Utf8String,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum CurveType {
Bls12_381(Bls12_381PointType),
}
impl Display for CurveType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CurveType::Bls12_381(point) => write!(f, "<Bls12_381, {point}>"),
}
}
}
impl From<&Curve> for CurveType {
fn from(value: &Curve) -> Self {
match value {
Curve::Bls12_381(point) => CurveType::Bls12_381(point.into()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum Bls12_381PointType {
G1,
G2,
}
impl From<&Bls12_381Point> for Bls12_381PointType {
fn from(value: &Bls12_381Point) -> Self {
match value {
Bls12_381Point::G1(_) => Bls12_381PointType::G1,
Bls12_381Point::G2(_) => Bls12_381PointType::G2,
}
}
}
impl Display for Bls12_381PointType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Bls12_381PointType::G1 => write!(f, "G1"),
Bls12_381PointType::G2 => write!(f, "G2"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum Curve {
Bls12_381(Bls12_381Point),
}
impl Curve {
pub fn compress(&self) -> Vec<u8> {
match self {
Curve::Bls12_381(point) => match point {
Bls12_381Point::G1(g1) => g1.compress(),
Bls12_381Point::G2(g2) => g2.compress(),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum Bls12_381Point {
G1(blst::blst_p1),
G2(blst::blst_p2),
}
impl Default for Bls12_381Point {
fn default() -> Self {
Bls12_381Point::G1(Default::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum AssignmentKind {
Let,

View File

@ -4,7 +4,7 @@ use vec1::Vec1;
use crate::{
ast::{
self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, Curve,
DefinitionLocation, IfBranch, Located, LogicalOpChainKind, ParsedCallArg, Pattern,
RecordUpdateSpread, Span, TraceKind, TypedClause, TypedRecordUpdateArg, UnOp,
UntypedClause, UntypedRecordUpdateArg,
@ -34,6 +34,12 @@ pub enum TypedExpr {
bytes: Vec<u8>,
},
CurvePoint {
location: Span,
tipo: Rc<Type>,
point: Curve,
},
Sequence {
location: Span,
expressions: Vec<Self>,
@ -186,7 +192,8 @@ impl TypedExpr {
| Self::Assignment { tipo, .. }
| Self::ModuleSelect { tipo, .. }
| Self::RecordAccess { tipo, .. }
| Self::RecordUpdate { tipo, .. } => tipo.clone(),
| Self::RecordUpdate { tipo, .. }
| Self::CurvePoint { tipo, .. } => tipo.clone(),
Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => {
expressions.last().map(TypedExpr::tipo).unwrap_or_else(void)
}
@ -227,7 +234,8 @@ impl TypedExpr {
| TypedExpr::ByteArray { .. }
| TypedExpr::Assignment { .. }
| TypedExpr::TupleIndex { .. }
| TypedExpr::RecordAccess { .. } => None,
| TypedExpr::RecordAccess { .. }
| TypedExpr::CurvePoint { .. } => None,
TypedExpr::If { .. } => None,
// TODO: test
@ -269,7 +277,8 @@ impl TypedExpr {
| Self::TupleIndex { location, .. }
| Self::ModuleSelect { location, .. }
| Self::RecordAccess { location, .. }
| Self::RecordUpdate { location, .. } => *location,
| Self::RecordUpdate { location, .. }
| Self::CurvePoint { location, .. } => *location,
Self::If { branches, .. } => branches.first().body.type_defining_location(),
@ -306,7 +315,8 @@ impl TypedExpr {
| Self::TupleIndex { location, .. }
| Self::ModuleSelect { location, .. }
| Self::RecordAccess { location, .. }
| Self::RecordUpdate { location, .. } => *location,
| Self::RecordUpdate { location, .. }
| Self::CurvePoint { location, .. } => *location,
}
}
@ -323,7 +333,8 @@ impl TypedExpr {
| TypedExpr::UInt { .. }
| TypedExpr::String { .. }
| TypedExpr::ByteArray { .. }
| TypedExpr::ModuleSelect { .. } => Some(Located::Expression(self)),
| TypedExpr::ModuleSelect { .. }
| TypedExpr::CurvePoint { .. } => Some(Located::Expression(self)),
TypedExpr::Trace { text, then, .. } => text
.find_node(byte_index)
@ -472,6 +483,12 @@ pub enum UntypedExpr {
preferred_format: ByteArrayFormatPreference,
},
CurvePoint {
location: Span,
point: Curve,
preferred_format: ByteArrayFormatPreference,
},
PipeLine {
expressions: Vec1<Self>,
one_liner: bool,
@ -733,7 +750,8 @@ impl UntypedExpr {
| Self::RecordUpdate { location, .. }
| Self::UnOp { location, .. }
| Self::LogicalOpChain { location, .. }
| Self::If { location, .. } => *location,
| Self::If { location, .. }
| Self::CurvePoint { location, .. } => *location,
Self::Sequence {
location,
expressions,

View File

@ -1,11 +1,11 @@
use crate::{
ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
ClauseGuard, Constant, DataType, Definition, Function, IfBranch, LogicalOpChainKind,
ModuleConstant, Pattern, RecordConstructor, RecordConstructorArg, RecordUpdateSpread, Span,
TraceKind, TypeAlias, TypedArg, UnOp, UnqualifiedImport, UntypedArg, UntypedClause,
UntypedClauseGuard, UntypedDefinition, UntypedFunction, UntypedModule, UntypedPattern,
UntypedRecordUpdateArg, Use, Validator, CAPTURE_VARIABLE,
ClauseGuard, Constant, CurveType, DataType, Definition, Function, IfBranch,
LogicalOpChainKind, ModuleConstant, Pattern, RecordConstructor, RecordConstructorArg,
RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp, UnqualifiedImport,
UntypedArg, UntypedClause, UntypedClauseGuard, UntypedDefinition, UntypedFunction,
UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator, CAPTURE_VARIABLE,
},
docvec,
expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR},
@ -352,7 +352,12 @@ impl<'comments> Formatter<'comments> {
bytes,
preferred_format,
..
} => self.bytearray(bytes, preferred_format),
} => self.bytearray(bytes, None, preferred_format),
Constant::CurvePoint {
point,
preferred_format,
..
} => self.bytearray(&point.compress(), Some(point.into()), preferred_format),
Constant::Int { value, base, .. } => self.int(value, base),
Constant::String { value, .. } => self.string(value),
}
@ -676,17 +681,24 @@ impl<'comments> Formatter<'comments> {
pub fn bytearray<'a>(
&mut self,
bytes: &'a [u8],
bytes: &[u8],
curve: Option<CurveType>,
preferred_format: &ByteArrayFormatPreference,
) -> Document<'a> {
match preferred_format {
ByteArrayFormatPreference::HexadecimalString => "#"
.to_doc()
.append(Document::String(
curve.map(|c| c.to_string()).unwrap_or_default(),
))
.append("\"")
.append(Document::String(hex::encode(bytes)))
.append("\""),
ByteArrayFormatPreference::ArrayOfBytes(Base::Decimal { .. }) => "#"
.to_doc()
.append(Document::String(
curve.map(|c| c.to_string()).unwrap_or_default(),
))
.append(
flex_break("[", "[")
.append(join(bytes.iter().map(|b| b.to_doc()), break_(",", ", ")))
@ -697,6 +709,9 @@ impl<'comments> Formatter<'comments> {
.group(),
ByteArrayFormatPreference::ArrayOfBytes(Base::Hexadecimal) => "#"
.to_doc()
.append(Document::String(
curve.map(|c| c.to_string()).unwrap_or_default(),
))
.append(
flex_break("[", "[")
.append(join(
@ -771,7 +786,13 @@ impl<'comments> Formatter<'comments> {
bytes,
preferred_format,
..
} => self.bytearray(bytes, preferred_format),
} => self.bytearray(bytes, None, preferred_format),
UntypedExpr::CurvePoint {
point,
preferred_format,
..
} => self.bytearray(&point.compress(), Some(point.into()), preferred_format),
UntypedExpr::If {
branches,

View File

@ -1,4 +1,5 @@
use chumsky::prelude::*;
use uplc::machine::runtime::Compressable;
use crate::{
ast,
@ -45,13 +46,36 @@ pub fn value() -> impl Parser<Token, ast::Constant, Error = ParseError> {
base,
});
let constant_bytearray_parser =
literal::bytearray(
|bytes, preferred_format, location| ast::Constant::ByteArray {
let constant_bytearray_parser = literal::bytearray(
|bytes, preferred_format, curve, location, emit| match curve {
Some(curve @ ast::CurveType::Bls12_381(point)) => {
let point = match point {
ast::Bls12_381PointType::G1 => {
blst::blst_p1::uncompress(&bytes).map(ast::Bls12_381Point::G1)
}
ast::Bls12_381PointType::G2 => {
blst::blst_p2::uncompress(&bytes).map(ast::Bls12_381Point::G2)
}
};
let point = point.unwrap_or_else(|_err| {
emit(ParseError::point_not_on_curve(curve, location));
ast::Bls12_381Point::default()
});
ast::Constant::CurvePoint {
location,
point: ast::Curve::Bls12_381(point),
preferred_format,
}
}
None => ast::Constant::ByteArray {
location,
bytes,
preferred_format,
},
},
);
choice((

View File

@ -1,4 +1,7 @@
use crate::{ast::Span, parser::token::Token};
use crate::{
ast::{CurveType, Span},
parser::token::Token,
};
use indoc::formatdoc;
use miette::Diagnostic;
use owo_colors::{OwoColorize, Stream::Stdout};
@ -46,6 +49,32 @@ impl ParseError {
}
}
pub fn point_not_on_curve(curve: CurveType, span: Span) -> Self {
Self {
kind: ErrorKind::PointNotOnCurve { curve },
span,
while_parsing: None,
expected: HashSet::new(),
label: Some("out off curve"),
}
}
pub fn unknown_point_curve(curve: String, point: Option<String>, span: Span) -> Self {
let label = if point.is_some() {
Some("unknown curve")
} else {
Some("unknown point")
};
Self {
kind: ErrorKind::UnknownCurvePoint { curve, point },
span,
while_parsing: None,
expected: HashSet::new(),
label,
}
}
pub fn malformed_base16_string_literal(span: Span) -> Self {
Self {
kind: ErrorKind::MalformedBase16StringLiteral,
@ -134,6 +163,15 @@ pub enum ErrorKind {
hint: Option<String>,
},
#[error("I tripped over a {}", fmt_curve_type(.curve))]
PointNotOnCurve { curve: CurveType },
#[error("I tripped over a {}", fmt_unknown_curve(.curve, .point))]
UnknownCurvePoint {
curve: String,
point: Option<String>,
},
#[error("I tripped over a malformed hexadecimal digits.")]
#[diagnostic(help("{}", formatdoc! {
r#"When numbers starts with '0x', they are treated as hexadecimal numbers. Thus, only digits from 0-9 or letter from a-f (or A-F) can be used following a '0x' number declaration. Plus, hexadecimal digits always go by pairs, so the total number of digits must be even (not counting leading zeros)."#
@ -186,6 +224,32 @@ pub enum ErrorKind {
InvalidWhenClause,
}
fn fmt_curve_type(curve: &CurveType) -> String {
match curve {
CurveType::Bls12_381(point) => {
format!("{point} point that is not in the bls12_381 curve")
}
}
}
fn fmt_unknown_curve(curve: &String, point: &Option<String>) -> String {
match point {
Some(point) => {
format!(
"{} which is an unknown point for curve {}",
point.if_supports_color(Stdout, |s| s.purple()),
curve.if_supports_color(Stdout, |s| s.purple()),
)
}
None => {
format!(
"{} which is an unknown curve",
curve.if_supports_color(Stdout, |s| s.purple())
)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]
pub enum Pattern {
#[error("I found an unexpected char '{0:?}'.")]

View File

@ -1,13 +1,43 @@
use chumsky::prelude::*;
use uplc::machine::runtime::Compressable;
use crate::parser::{error::ParseError, expr::UntypedExpr, literal::bytearray, token::Token};
use crate::{
ast,
parser::{error::ParseError, expr::UntypedExpr, literal::bytearray, token::Token},
};
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
bytearray(|bytes, preferred_format, location| UntypedExpr::ByteArray {
bytearray(
|bytes, preferred_format, curve, location, emit| match curve {
Some(curve @ ast::CurveType::Bls12_381(point)) => {
let point = match point {
ast::Bls12_381PointType::G1 => {
blst::blst_p1::uncompress(&bytes).map(ast::Bls12_381Point::G1)
}
ast::Bls12_381PointType::G2 => {
blst::blst_p2::uncompress(&bytes).map(ast::Bls12_381Point::G2)
}
};
let point = point.unwrap_or_else(|_err| {
emit(ParseError::point_not_on_curve(curve, location));
ast::Bls12_381Point::default()
});
UntypedExpr::CurvePoint {
location,
point: ast::Curve::Bls12_381(point),
preferred_format,
}
}
None => UntypedExpr::ByteArray {
location,
bytes,
preferred_format,
})
},
},
)
}
#[cfg(test)]

View File

@ -9,16 +9,70 @@ use crate::{
};
pub fn parser<A>(
into: impl Fn(Vec<u8>, ast::ByteArrayFormatPreference, ast::Span) -> A,
into: impl Fn(
Vec<u8>,
ast::ByteArrayFormatPreference,
Option<ast::CurveType>,
ast::Span,
&mut dyn FnMut(ParseError),
) -> A,
) -> impl Parser<Token, A, Error = ParseError> {
choice((array_of_bytes(), hex_string(), utf8_string()))
.map_with_span(move |(preferred_format, bytes), span| into(bytes, preferred_format, span))
choice((
array_of_bytes(),
hex_string(),
utf8_string().map(|(p, b)| (None, p, b)),
))
.validate(move |(curve, preferred_format, bytes), span, emit| {
into(bytes, preferred_format, curve, span, emit)
})
}
pub fn array_of_bytes(
) -> impl Parser<Token, (ast::ByteArrayFormatPreference, Vec<u8>), Error = ParseError> {
fn curve_point() -> impl Parser<Token, ast::CurveType, Error = ParseError> {
just(Token::Less)
.ignore_then(select! {Token::UpName {name} => name})
.then_ignore(just(Token::Comma))
.then(select! {Token::UpName {name} => name})
.then_ignore(just(Token::Greater))
.validate(
|(curve_type, point_type), span, emit| match curve_type.as_str() {
"Bls12_381" => {
let point = match point_type.as_str() {
"G1" => ast::Bls12_381PointType::G1,
"G2" => ast::Bls12_381PointType::G2,
_ => {
emit(ParseError::unknown_point_curve(
curve_type,
Some(point_type),
span,
));
ast::Bls12_381PointType::G1
}
};
ast::CurveType::Bls12_381(point)
}
_ => {
emit(ParseError::unknown_point_curve(curve_type, None, span));
ast::CurveType::Bls12_381(ast::Bls12_381PointType::G1)
}
},
)
}
pub fn array_of_bytes() -> impl Parser<
Token,
(
Option<ast::CurveType>,
ast::ByteArrayFormatPreference,
Vec<u8>,
),
Error = ParseError,
> {
just(Token::Hash)
.ignore_then(
.ignore_then(curve_point().or_not())
.then(
select! {Token::Int {value, base, ..} => (value, base)}
.validate(|(value, base), span, emit| {
let byte: u8 = match value.parse() {
@ -38,7 +92,7 @@ pub fn array_of_bytes(
.allow_trailing()
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
)
.validate(|bytes, span, emit| {
.validate(|(curve, bytes), span, emit| {
let base = bytes.iter().try_fold(None, |acc, (_, base)| match acc {
None => Ok(Some(base)),
Some(previous_base) if previous_base == base => Ok(Some(base)),
@ -58,15 +112,33 @@ pub fn array_of_bytes(
Ok(Some(base)) => *base,
};
(bytes.into_iter().map(|(b, _)| b).collect::<Vec<u8>>(), base)
(
curve,
bytes.into_iter().map(|(b, _)| b).collect::<Vec<u8>>(),
base,
)
})
.map(|(curve, bytes, base)| {
(
curve,
ast::ByteArrayFormatPreference::ArrayOfBytes(base),
bytes,
)
})
.map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes))
}
pub fn hex_string(
) -> impl Parser<Token, (ast::ByteArrayFormatPreference, Vec<u8>), Error = ParseError> {
pub fn hex_string() -> impl Parser<
Token,
(
Option<ast::CurveType>,
ast::ByteArrayFormatPreference,
Vec<u8>,
),
Error = ParseError,
> {
just(Token::Hash)
.ignore_then(
.ignore_then(curve_point().or_not())
.then(
select! {Token::ByteString {value} => value}.validate(|value, span, emit| {
match hex::decode(value) {
Ok(bytes) => bytes,
@ -77,7 +149,13 @@ pub fn hex_string(
}
}),
)
.map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token))
.map(|(curve, token)| {
(
curve,
ast::ByteArrayFormatPreference::HexadecimalString,
token,
)
})
}
pub fn utf8_string(

View File

@ -3,13 +3,13 @@ use vec1::Vec1;
use crate::{
ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg,
ClauseGuard, Constant, IfBranch, LogicalOpChainKind, RecordUpdateSpread, Span, TraceKind,
Tracing, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch,
TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard,
UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
Annotation, Arg, ArgName, AssignmentKind, BinOp, Bls12_381Point, ByteArrayFormatPreference,
CallArg, ClauseGuard, Constant, Curve, IfBranch, LogicalOpChainKind, RecordUpdateSpread,
Span, TraceKind, Tracing, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard,
TypedIfBranch, TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause,
UntypedClauseGuard, UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
},
builtins::{bool, byte_array, function, int, list, string, tuple},
builtins::{bool, byte_array, function, g1_element, g2_element, int, list, string, tuple},
expr::{FnStyle, TypedExpr, UntypedExpr},
format,
tipo::fields::FieldMap,
@ -321,6 +321,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location,
} => self.infer_bytearray(bytes, preferred_format, location),
UntypedExpr::CurvePoint {
location, point, ..
} => self.infer_curve_point(point, location),
UntypedExpr::RecordUpdate {
location,
constructor,
@ -363,6 +367,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
})
}
fn infer_curve_point(&mut self, curve: Curve, location: Span) -> Result<TypedExpr, Error> {
let tipo = match curve {
Curve::Bls12_381(point) => match point {
Bls12_381Point::G1(_) => g1_element(),
Bls12_381Point::G2(_) => g2_element(),
},
};
Ok(TypedExpr::CurvePoint {
location,
point: curve,
tipo,
})
}
fn infer_trace_if_false(
&mut self,
value: UntypedExpr,
@ -1345,6 +1364,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
preferred_format,
})
}
Constant::CurvePoint {
location,
point,
preferred_format,
} => Ok(Constant::CurvePoint {
location,
point,
preferred_format,
}),
}?;
// Check type annotation is accurate.
@ -2000,7 +2028,8 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> {
| UntypedExpr::Var { .. }
| UntypedExpr::LogicalOpChain { .. }
| UntypedExpr::TraceIfFalse { .. }
| UntypedExpr::When { .. } => Ok(()),
| UntypedExpr::When { .. }
| UntypedExpr::CurvePoint { .. } => Ok(()),
}
}
fn assert_assignment(expr: &UntypedExpr) -> Result<(), Error> {