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

View File

@@ -1,4 +1,5 @@
use chumsky::prelude::*;
use uplc::machine::runtime::Compressable;
use crate::{
ast,
@@ -45,14 +46,37 @@ 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((
constant_string_parser,

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 {
location,
bytes,
preferred_format,
})
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(