From 796bb219dac4f36d01c143aa20becabceef1ed32 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 16 Apr 2024 16:04:47 -0400 Subject: [PATCH] Public keys, pubkey operations, p2 replica --- crates/rue-compiler/src/codegen.rs | 19 ++ crates/rue-compiler/src/error.rs | 3 + crates/rue-compiler/src/hir.rs | 26 +- crates/rue-compiler/src/lir.rs | 2 + crates/rue-compiler/src/lowerer.rs | 310 ++++++++++++++++---- crates/rue-compiler/src/optimizer.rs | 42 ++- crates/rue-compiler/src/ty.rs | 2 + crates/rue-lexer/src/lib.rs | 2 + crates/rue-lexer/src/token_kind.rs | 1 + crates/rue-parser/src/ast.rs | 9 +- crates/rue-parser/src/grammar.rs | 18 +- crates/rue-parser/src/parser.rs | 2 + crates/rue-parser/src/syntax_kind.rs | 4 + examples/puzzles/non_standard_p2.rue | 38 +++ examples/{ => puzzles}/signature_puzzle.rue | 1 + tests.toml | 7 + 16 files changed, 385 insertions(+), 101 deletions(-) create mode 100644 examples/puzzles/non_standard_p2.rue rename examples/{ => puzzles}/signature_puzzle.rue (99%) diff --git a/crates/rue-compiler/src/codegen.rs b/crates/rue-compiler/src/codegen.rs index fb357a1..01be94e 100644 --- a/crates/rue-compiler/src/codegen.rs +++ b/crates/rue-compiler/src/codegen.rs @@ -30,6 +30,8 @@ struct Ops { div: NodePtr, divmod: NodePtr, gt: NodePtr, + point_add: NodePtr, + pubkey_for_exp: NodePtr, not: NodePtr, any: NodePtr, } @@ -55,6 +57,8 @@ impl<'a> Codegen<'a> { div: allocator.new_small_number(19).unwrap(), divmod: allocator.new_small_number(20).unwrap(), gt: allocator.new_small_number(21).unwrap(), + point_add: allocator.new_small_number(29).unwrap(), + pubkey_for_exp: allocator.new_small_number(30).unwrap(), not: allocator.new_small_number(32).unwrap(), any: allocator.new_small_number(33).unwrap(), }; @@ -76,7 +80,9 @@ impl<'a> Codegen<'a> { Lir::Sha256(value) => self.gen_sha256(value), Lir::IsCons(value) => self.gen_is_cons(value), Lir::Strlen(value) => self.gen_strlen(value), + Lir::PubkeyForExp(value) => self.gen_pubkey_for_exp(value), Lir::Concat(values) => self.gen_concat(values), + Lir::PointAdd(values) => self.gen_point_add(values), Lir::If(condition, then_branch, else_branch) => { self.gen_if(condition, then_branch, else_branch) } @@ -161,6 +167,11 @@ impl<'a> Codegen<'a> { self.list(&[self.ops.strlen, value]) } + fn gen_pubkey_for_exp(&mut self, value: LirId) -> NodePtr { + let value = self.gen_lir(value); + self.list(&[self.ops.pubkey_for_exp, value]) + } + fn gen_concat(&mut self, values: Vec) -> NodePtr { let mut args = vec![self.ops.concat]; for value in values { @@ -169,6 +180,14 @@ impl<'a> Codegen<'a> { self.list(&args) } + fn gen_point_add(&mut self, values: Vec) -> NodePtr { + let mut args = vec![self.ops.point_add]; + for value in values { + args.push(self.gen_lir(value)); + } + self.list(&args) + } + fn gen_if(&mut self, condition: LirId, then_branch: LirId, else_branch: LirId) -> NodePtr { let condition = self.gen_lir(condition); let then_branch = self.gen_lir(then_branch); diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 212954f..9e40d20 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -127,6 +127,9 @@ pub enum DiagnosticInfo { #[error("blocks must either have a an expression value, return statement, or raise an error")] EmptyBlock, + + #[error("cannot check equality on non-atom type `{0}`")] + NonAtomEquality(String), } /// Join a list of names into a string, wrapped in backticks. diff --git a/crates/rue-compiler/src/hir.rs b/crates/rue-compiler/src/hir.rs index f7c499f..9619305 100644 --- a/crates/rue-compiler/src/hir.rs +++ b/crates/rue-compiler/src/hir.rs @@ -1,5 +1,3 @@ -use rue_parser::BinaryOp; - use crate::database::{HirId, ScopeId, SymbolId}; #[derive(Debug, Clone)] @@ -17,7 +15,7 @@ pub enum Hir { args: HirId, }, BinaryOp { - op: HirBinaryOp, + op: BinOp, lhs: HirId, rhs: HirId, }, @@ -28,6 +26,7 @@ pub enum Hir { Sha256(HirId), IsCons(HirId), Strlen(HirId), + PubkeyForExp(HirId), If { condition: HirId, then_block: HirId, @@ -36,7 +35,7 @@ pub enum Hir { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum HirBinaryOp { +pub enum BinOp { Add, Subtract, Multiply, @@ -49,22 +48,5 @@ pub enum HirBinaryOp { Equals, NotEquals, Concat, -} - -impl From for HirBinaryOp { - fn from(op: BinaryOp) -> Self { - match op { - BinaryOp::Add => HirBinaryOp::Add, - BinaryOp::Subtract => HirBinaryOp::Subtract, - BinaryOp::Multiply => HirBinaryOp::Multiply, - BinaryOp::Divide => HirBinaryOp::Divide, - BinaryOp::Remainder => HirBinaryOp::Remainder, - BinaryOp::LessThan => HirBinaryOp::LessThan, - BinaryOp::GreaterThan => HirBinaryOp::GreaterThan, - BinaryOp::LessThanEquals => HirBinaryOp::LessThanEquals, - BinaryOp::GreaterThanEquals => HirBinaryOp::GreaterThanEquals, - BinaryOp::Equals => HirBinaryOp::Equals, - BinaryOp::NotEquals => HirBinaryOp::NotEquals, - } - } + PointAdd, } diff --git a/crates/rue-compiler/src/lir.rs b/crates/rue-compiler/src/lir.rs index e2f90e1..a7f95a3 100644 --- a/crates/rue-compiler/src/lir.rs +++ b/crates/rue-compiler/src/lir.rs @@ -15,10 +15,12 @@ pub enum Lir { Sha256(LirId), IsCons(LirId), Strlen(LirId), + PubkeyForExp(LirId), If(LirId, LirId, LirId), Not(LirId), Any(Vec), Concat(Vec), + PointAdd(Vec), Add(Vec), Sub(Vec), Mul(Vec), diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 035a4a0..509dff2 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -11,13 +11,13 @@ use rue_parser::{ AstNode, BinaryExpr, BinaryOp, Block, CastExpr, ConstItem, EnumItem, Expr, FieldAccess, FunctionCall, FunctionItem, FunctionType as AstFunctionType, GroupExpr, GuardExpr, IfExpr, IfStmt, IndexAccess, InitializerExpr, InitializerField, Item, LambdaExpr, LetStmt, ListExpr, - ListType, LiteralExpr, PairExpr, PairType, Path, PrefixExpr, PrefixOp, Root, Stmt, StructField, - StructItem, SyntaxKind, SyntaxToken, Type as AstType, TypeAliasItem, + ListType, LiteralExpr, OptionalType, PairExpr, PairType, Path, PrefixExpr, PrefixOp, Root, + Stmt, StructField, StructItem, SyntaxKind, SyntaxToken, Type as AstType, TypeAliasItem, }; use crate::{ database::{Database, HirId, ScopeId, SymbolId, TypeId}, - hir::{Hir, HirBinaryOp}, + hir::{BinOp, Hir}, scope::Scope, symbol::Symbol, ty::{EnumType, EnumVariant, FunctionType, Guard, StructType, Type, Value}, @@ -36,6 +36,7 @@ pub struct Lowerer<'a> { bool_type: TypeId, bytes_type: TypeId, bytes32_type: TypeId, + pk_type: TypeId, nil_type: TypeId, nil_hir: HirId, unknown_type: TypeId, @@ -48,6 +49,7 @@ impl<'a> Lowerer<'a> { let bool_type = db.alloc_type(Type::Bool); let bytes_type = db.alloc_type(Type::Bytes); let bytes32_type = db.alloc_type(Type::Bytes32); + let pk_type = db.alloc_type(Type::PublicKey); let any_type = db.alloc_type(Type::Any); let nil_type = db.alloc_type(Type::Nil); let nil_hir = db.alloc_hir(Hir::Atom(Vec::new())); @@ -61,6 +63,7 @@ impl<'a> Lowerer<'a> { builtins.define_type("Bool".to_string(), bool_type); builtins.define_type("Bytes".to_string(), bytes_type); builtins.define_type("Bytes32".to_string(), bytes32_type); + builtins.define_type("PublicKey".to_string(), pk_type); builtins.define_type("Any".to_string(), any_type); // Define the `sha256` function, since it's not an operator or keyword. @@ -84,6 +87,27 @@ impl<'a> Lowerer<'a> { ); } + // Define the `pubkey_for_exp` function, since it's not an operator or keyword. + { + let mut scope = Scope::default(); + let param = db.alloc_symbol(Symbol::Parameter { + type_id: bytes32_type, + }); + scope.define_symbol("exponent".to_string(), param); + let param_ref = db.alloc_hir(Hir::Reference(param)); + let hir_id = db.alloc_hir(Hir::PubkeyForExp(param_ref)); + let scope_id = db.alloc_scope(scope); + + builtins.define_symbol( + "pubkey_for_exp".to_string(), + db.alloc_symbol(Symbol::Function { + scope_id, + hir_id, + ty: FunctionType::new(vec![bytes32_type], pk_type, false), + }), + ); + } + let builtins_id = db.alloc_scope(builtins); Self { @@ -96,6 +120,7 @@ impl<'a> Lowerer<'a> { bool_type, bytes_type, bytes32_type, + pk_type, nil_type, nil_hir, unknown_type, @@ -931,67 +956,161 @@ impl<'a> Lowerer<'a> { } fn compile_binary_expr(&mut self, binary: BinaryExpr) -> Value { - let lhs = binary.lhs().map(|lhs| self.compile_expr(lhs, None)); - let rhs = binary.rhs().map(|rhs| self.compile_expr(rhs, None)); - - let lhs_ty = lhs - .as_ref() - .map(|lhs| lhs.ty()) - .unwrap_or(self.unknown_type); + let lhs = binary + .lhs() + .map(|lhs| self.compile_expr(lhs, None)) + .unwrap_or_else(|| self.unknown()); - let rhs_ty = rhs - .as_ref() - .map(|rhs| rhs.ty()) - .unwrap_or(self.unknown_type); + let rhs = binary + .rhs() + .map(|rhs| self.compile_expr(rhs, None)) + .unwrap_or_else(|| self.unknown()); - let mut op = binary.op().map(HirBinaryOp::from); + let Some(op) = binary.op() else { + return self.unknown(); + }; - let ty = if binary.op() == Some(BinaryOp::Add) - && self.is_assignable_to(lhs_ty, self.bytes_type, false, &mut HashSet::new()) - { - self.type_check(rhs_ty, self.bytes_type, binary.syntax().text_range()); + let text_range = binary.syntax().text_range(); + + let mut guards = HashMap::new(); + + let (op, ty) = match op { + BinaryOp::Add => { + if self.is_assignable_to(lhs.ty(), self.pk_type, false, &mut HashSet::new()) { + self.type_check(rhs.ty(), self.pk_type, text_range); + (BinOp::PointAdd, self.pk_type) + } else if self.is_assignable_to( + lhs.ty(), + self.bytes_type, + false, + &mut HashSet::new(), + ) { + self.type_check(rhs.ty(), self.bytes_type, text_range); + (BinOp::Concat, self.bytes_type) + } else { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::Add, self.int_type) + } + } + BinaryOp::Subtract => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::Subtract, self.int_type) + } + BinaryOp::Multiply => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::Multiply, self.int_type) + } + BinaryOp::Divide => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::Divide, self.int_type) + } + BinaryOp::Remainder => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::Remainder, self.int_type) + } + BinaryOp::Equals => { + if !self.is_atomic(lhs.ty(), &mut IndexSet::new()) + || !self.is_atomic(rhs.ty(), &mut IndexSet::new()) + { + self.error( + DiagnosticInfo::NonAtomEquality(self.type_name(lhs.ty())), + text_range, + ); + } else { + self.type_check(rhs.ty(), lhs.ty(), text_range); + } - op = Some(HirBinaryOp::Concat); + if self.is_assignable_to(lhs.ty(), self.nil_type, false, &mut HashSet::new()) { + if let Hir::Reference(symbol_id) = self.db.hir(rhs.hir()) { + guards.insert( + *symbol_id, + Guard::new(self.nil_type, self.try_unwrap_optional(rhs.ty())), + ); + } + } else if self.is_assignable_to(rhs.ty(), self.nil_type, false, &mut HashSet::new()) + { + if let Hir::Reference(symbol_id) = self.db.hir(lhs.hir()) { + guards.insert( + *symbol_id, + Guard::new(self.nil_type, self.try_unwrap_optional(lhs.ty())), + ); + } + } - Some(self.bytes_type) - } else { - self.type_check(lhs_ty, self.int_type, binary.syntax().text_range()); + (BinOp::Equals, self.bool_type) + } + BinaryOp::NotEquals => { + if !self.is_atomic(lhs.ty(), &mut IndexSet::new()) + || !self.is_atomic(rhs.ty(), &mut IndexSet::new()) + { + self.error( + DiagnosticInfo::NonAtomEquality(self.type_name(lhs.ty())), + text_range, + ); + } else { + self.type_check(rhs.ty(), lhs.ty(), text_range); + } - self.type_check(rhs_ty, self.int_type, binary.syntax().text_range()); + if self.is_assignable_to(lhs.ty(), self.nil_type, false, &mut HashSet::new()) { + if let Hir::Reference(symbol_id) = self.db.hir(rhs.hir()) { + guards.insert( + *symbol_id, + Guard::new(self.try_unwrap_optional(rhs.ty()), self.nil_type), + ); + } + } else if self.is_assignable_to(rhs.ty(), self.nil_type, false, &mut HashSet::new()) + { + if let Hir::Reference(symbol_id) = self.db.hir(lhs.hir()) { + guards.insert( + *symbol_id, + Guard::new(self.try_unwrap_optional(lhs.ty()), self.nil_type), + ); + } + } - None + (BinOp::NotEquals, self.bool_type) + } + BinaryOp::GreaterThan => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::GreaterThan, self.bool_type) + } + BinaryOp::LessThan => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::LessThan, self.bool_type) + } + BinaryOp::GreaterThanEquals => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::GreaterThanEquals, self.bool_type) + } + BinaryOp::LessThanEquals => { + self.type_check(lhs.ty(), self.int_type, text_range); + self.type_check(rhs.ty(), self.int_type, text_range); + (BinOp::LessThanEquals, self.bool_type) + } }; - let ty = ty.unwrap_or_else(|| { - binary - .op() - .map(|op| match op { - BinaryOp::Add => self.int_type, - BinaryOp::Subtract => self.int_type, - BinaryOp::Multiply => self.int_type, - BinaryOp::Divide => self.int_type, - BinaryOp::Remainder => self.int_type, - BinaryOp::LessThan => self.bool_type, - BinaryOp::GreaterThan => self.bool_type, - BinaryOp::LessThanEquals => self.bool_type, - BinaryOp::GreaterThanEquals => self.bool_type, - BinaryOp::Equals => self.bool_type, - BinaryOp::NotEquals => self.bool_type, - }) - .unwrap_or(self.unknown_type) - }); + let mut value = Value::typed( + self.db.alloc_hir(Hir::BinaryOp { + op, + lhs: lhs.hir(), + rhs: rhs.hir(), + }), + ty, + ); - match (lhs, rhs, op) { - (Some(lhs), Some(rhs), Some(op)) => Value::typed( - self.db.alloc_hir(Hir::BinaryOp { - op, - lhs: lhs.hir(), - rhs: rhs.hir(), - }), - ty, - ), - _ => Value::typed(self.unknown_hir, ty), + for (symbol_id, guard) in guards { + value.guard(symbol_id, guard); } + + value } fn compile_group_expr( @@ -1111,7 +1230,17 @@ impl<'a> Lowerer<'a> { let strlen = self.db.alloc_hir(Hir::Strlen(hir_id)); let length = self.db.alloc_hir(Hir::Atom(vec![32])); let hir_id = self.db.alloc_hir(Hir::BinaryOp { - op: HirBinaryOp::Equals, + op: BinOp::Equals, + lhs: strlen, + rhs: length, + }); + Some((Guard::new(to, from), hir_id)) + } + (Type::Bytes, Type::PublicKey) => { + let strlen = self.db.alloc_hir(Hir::Strlen(hir_id)); + let length = self.db.alloc_hir(Hir::Atom(vec![48])); + let hir_id = self.db.alloc_hir(Hir::BinaryOp { + op: BinOp::Equals, lhs: strlen, rhs: length, }); @@ -1477,7 +1606,7 @@ impl<'a> Lowerer<'a> { Symbol::Function { ty, .. } => self.db.alloc_type(Type::Function(ty.clone())), Symbol::Parameter { type_id } => *type_id, Symbol::LetBinding { type_id, .. } => *type_id, - Symbol::ConstBinding { type_id, .. } => *type_id, //todo + Symbol::ConstBinding { type_id, .. } => *type_id, }), ) } @@ -1649,6 +1778,7 @@ impl<'a> Lowerer<'a> { AstType::ListType(list) => self.compile_list_type(list), AstType::FunctionType(function) => self.compile_function_type(function), AstType::PairType(tuple) => self.compile_pair_type(tuple), + AstType::OptionalType(optional) => self.compile_optional_type(optional), } } @@ -1754,6 +1884,22 @@ impl<'a> Lowerer<'a> { ))) } + fn compile_optional_type(&mut self, optional: OptionalType) -> TypeId { + let ty = optional + .ty() + .map(|ty| self.compile_type(ty)) + .unwrap_or(self.unknown_type); + + self.db.alloc_type(Type::Optional(ty)) + } + + fn try_unwrap_optional(&mut self, ty: TypeId) -> TypeId { + match self.db.ty(ty) { + Type::Optional(inner) => self.try_unwrap_optional(*inner), + _ => ty, + } + } + fn detect_cycle( &mut self, ty: TypeId, @@ -1783,7 +1929,9 @@ impl<'a> Lowerer<'a> { | Type::Int | Type::Bool | Type::Bytes - | Type::Bytes32 => false, + | Type::Bytes32 + | Type::PublicKey => false, + Type::Optional(ty) => self.detect_cycle(ty, text_range, visited_aliases), } } @@ -1812,6 +1960,7 @@ impl<'a> Lowerer<'a> { Type::Bool => "Bool".to_string(), Type::Bytes => "Bytes".to_string(), Type::Bytes32 => "Bytes32".to_string(), + Type::PublicKey => "PublicKey".to_string(), Type::List(items) => { let inner = self.type_name_visitor(*items, stack); format!("{}[]", inner) @@ -1859,6 +2008,10 @@ impl<'a> Lowerer<'a> { format!("fun({}) -> {}", params.join(", "), ret) } Type::Alias(..) => unreachable!(), + Type::Optional(ty) => { + let inner = self.type_name_visitor(*ty, stack); + format!("{}?", inner) + } }; stack.pop().unwrap(); @@ -1911,14 +2064,16 @@ impl<'a> Lowerer<'a> { (Type::Any, Type::Any) => true, (Type::Int, Type::Int) => true, (Type::Bool, Type::Bool) => true, - (Type::Bytes, Type::Bytes) => true, + (Type::Bytes | Type::Nil, Type::Bytes) => true, (Type::Bytes32, Type::Bytes32 | Type::Bytes) => true, + (Type::PublicKey, Type::PublicKey) => true, (_, Type::Any) => true, // Primitive casts. - (Type::Nil, Type::Bytes | Type::Bool | Type::Int) if cast => true, + (Type::Nil, Type::Bool | Type::Int) if cast => true, (Type::Int, Type::Bytes) if cast => true, - (Type::Bytes | Type::Bytes32, Type::Int) if cast => true, + (Type::Bytes | Type::Bytes32 | Type::PublicKey, Type::Int) if cast => true, + (Type::PublicKey, Type::Bytes) if cast => true, (Type::Bool, Type::Int | Type::Bytes) if cast => true, (Type::Any, _) if cast => true, @@ -1952,6 +2107,17 @@ impl<'a> Lowerer<'a> { self.is_assignable_to(fun_a.return_type(), fun_b.return_type(), cast, visited) } + // Optional types are assignable to themselves. + (Type::Optional(inner_a), Type::Optional(inner_b)) => { + self.is_assignable_to(inner_a, inner_b, cast, visited) + } + + // Either nil or inner type is assignable to optionals. + (_, Type::Optional(inner_b)) => { + self.types_equal(a, self.nil_type) + || self.is_assignable_to(a, inner_b, cast, visited) + } + _ => false, } } @@ -1983,6 +2149,7 @@ impl<'a> Lowerer<'a> { Type::Bool => matches!(b, Type::Bool), Type::Bytes => matches!(b, Type::Bytes), Type::Bytes32 => matches!(b, Type::Bytes32), + Type::PublicKey => matches!(b, Type::PublicKey), Type::Enum(..) | Type::EnumVariant(..) | Type::Struct(..) => a_id == b_id, Type::List(inner) => { if let Type::List(other_inner) = b { @@ -2025,6 +2192,13 @@ impl<'a> Lowerer<'a> { } } Type::Alias(..) => unreachable!(), + Type::Optional(ty) => { + if let Type::Optional(other_ty) = b { + self.types_equal_visitor(ty, other_ty, visited) + } else { + false + } + } }; visited.remove(&key); @@ -2032,6 +2206,24 @@ impl<'a> Lowerer<'a> { equal } + fn is_atomic(&self, ty: TypeId, stack: &mut IndexSet) -> bool { + if !stack.insert(ty) { + return false; + } + + let is_atomic = match self.db.ty(ty) { + Type::Nil | Type::Int | Type::Bool | Type::Bytes | Type::Bytes32 | Type::PublicKey => { + true + } + Type::Optional(ty) => self.is_atomic(*ty, stack), + _ => false, + }; + + stack.pop().unwrap(); + + is_atomic + } + fn unknown(&self) -> Value { Value::typed(self.unknown_hir, self.unknown_type) } diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index f6f52f3..b247618 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -4,7 +4,7 @@ use indexmap::IndexSet; use crate::{ database::{Database, HirId, LirId, ScopeId, SymbolId}, - hir::{Hir, HirBinaryOp}, + hir::{BinOp, Hir}, lir::Lir, symbol::Symbol, }; @@ -63,7 +63,8 @@ impl<'a> Optimizer<'a> { | Hir::Not(value) | Hir::Sha256(value) | Hir::IsCons(value) - | Hir::Strlen(value) => self.compute_captures_hir(scope_id, value), + | Hir::Strlen(value) + | Hir::PubkeyForExp(value) => self.compute_captures_hir(scope_id, value), Hir::If { condition, then_block, @@ -304,18 +305,19 @@ impl<'a> Optimizer<'a> { Hir::FunctionCall { callee, args } => self.opt_function_call(scope_id, *callee, *args), Hir::BinaryOp { op, lhs, rhs } => { let handler = match op { - HirBinaryOp::Add => Self::opt_add, - HirBinaryOp::Subtract => Self::opt_subtract, - HirBinaryOp::Multiply => Self::opt_multiply, - HirBinaryOp::Divide => Self::opt_divide, - HirBinaryOp::Remainder => Self::opt_remainder, - HirBinaryOp::LessThan => Self::opt_lt, - HirBinaryOp::GreaterThan => Self::opt_gt, - HirBinaryOp::LessThanEquals => Self::opt_lteq, - HirBinaryOp::GreaterThanEquals => Self::opt_gteq, - HirBinaryOp::Equals => Self::opt_eq, - HirBinaryOp::NotEquals => Self::opt_neq, - HirBinaryOp::Concat => Self::opt_concat, + BinOp::Add => Self::opt_add, + BinOp::Subtract => Self::opt_subtract, + BinOp::Multiply => Self::opt_multiply, + BinOp::Divide => Self::opt_divide, + BinOp::Remainder => Self::opt_remainder, + BinOp::LessThan => Self::opt_lt, + BinOp::GreaterThan => Self::opt_gt, + BinOp::LessThanEquals => Self::opt_lteq, + BinOp::GreaterThanEquals => Self::opt_gteq, + BinOp::Equals => Self::opt_eq, + BinOp::NotEquals => Self::opt_neq, + BinOp::Concat => Self::opt_concat, + BinOp::PointAdd => Self::opt_point_add, }; handler(self, scope_id, *lhs, *rhs) } @@ -326,6 +328,7 @@ impl<'a> Optimizer<'a> { Hir::Sha256(value) => self.opt_sha256(scope_id, *value), Hir::IsCons(value) => self.opt_is_cons(scope_id, *value), Hir::Strlen(value) => self.opt_strlen(scope_id, *value), + Hir::PubkeyForExp(value) => self.opt_pubkey_for_exp(scope_id, *value), Hir::If { condition, then_block, @@ -365,6 +368,11 @@ impl<'a> Optimizer<'a> { self.db.alloc_lir(Lir::Strlen(lir_id)) } + fn opt_pubkey_for_exp(&mut self, scope_id: ScopeId, hir_id: HirId) -> LirId { + let lir_id = self.opt_hir(scope_id, hir_id); + self.db.alloc_lir(Lir::PubkeyForExp(lir_id)) + } + fn opt_reference(&mut self, scope_id: ScopeId, symbol_id: SymbolId) -> LirId { match self.db.symbol(symbol_id).clone() { Symbol::Function { @@ -487,6 +495,12 @@ impl<'a> Optimizer<'a> { self.db.alloc_lir(Lir::Concat(vec![lhs, rhs])) } + fn opt_point_add(&mut self, scope_id: ScopeId, lhs: HirId, rhs: HirId) -> LirId { + let lhs = self.opt_hir(scope_id, lhs); + let rhs = self.opt_hir(scope_id, rhs); + self.db.alloc_lir(Lir::PointAdd(vec![lhs, rhs])) + } + fn opt_not(&mut self, scope_id: ScopeId, value: HirId) -> LirId { let value = self.opt_hir(scope_id, value); self.db.alloc_lir(Lir::Not(value)) diff --git a/crates/rue-compiler/src/ty.rs b/crates/rue-compiler/src/ty.rs index fd66031..f041d54 100644 --- a/crates/rue-compiler/src/ty.rs +++ b/crates/rue-compiler/src/ty.rs @@ -16,6 +16,7 @@ pub enum Type { Bool, Bytes, Bytes32, + PublicKey, Pair(TypeId, TypeId), List(TypeId), Struct(StructType), @@ -23,6 +24,7 @@ pub enum Type { EnumVariant(EnumVariant), Function(FunctionType), Alias(TypeId), + Optional(TypeId), } #[derive(Debug, Clone)] diff --git a/crates/rue-lexer/src/lib.rs b/crates/rue-lexer/src/lib.rs index 3df7873..3f0bbcc 100644 --- a/crates/rue-lexer/src/lib.rs +++ b/crates/rue-lexer/src/lib.rs @@ -80,6 +80,7 @@ impl<'a> Lexer<'a> { '*' => self.block_comment(), _ => TokenKind::Slash, }, + '?' => TokenKind::Question, '.' => match self.peek() { '.' if self.peek_nth(1) == '.' => { self.bump(); @@ -308,6 +309,7 @@ mod tests { check("->", &[TokenKind::Arrow]); check("=>", &[TokenKind::FatArrow]); check("...", &[TokenKind::Spread]); + check("?", &[TokenKind::Question]); } #[test] diff --git a/crates/rue-lexer/src/token_kind.rs b/crates/rue-lexer/src/token_kind.rs index 700cd44..af82a55 100644 --- a/crates/rue-lexer/src/token_kind.rs +++ b/crates/rue-lexer/src/token_kind.rs @@ -36,6 +36,7 @@ pub enum TokenKind { Arrow, FatArrow, Spread, + Question, Plus, Minus, diff --git a/crates/rue-parser/src/ast.rs b/crates/rue-parser/src/ast.rs index 0d07dff..da99c6f 100644 --- a/crates/rue-parser/src/ast.rs +++ b/crates/rue-parser/src/ast.rs @@ -113,12 +113,13 @@ ast_node!(IndexAccess); ast_node!(LambdaExpr); ast_node!(LambdaParam); -ast_enum!(Type, Path, ListType, PairType, FunctionType); +ast_enum!(Type, Path, ListType, PairType, FunctionType, OptionalType); ast_node!(ListType); ast_node!(ListTypeItem); ast_node!(PairType); ast_node!(FunctionType); ast_node!(FunctionTypeParam); +ast_node!(OptionalType); ast_enum!(Stmt, LetStmt, IfStmt, ReturnStmt, RaiseStmt, AssertStmt); ast_node!(LetStmt); @@ -687,3 +688,9 @@ impl FunctionTypeParam { self.syntax().children().find_map(Type::cast) } } + +impl OptionalType { + pub fn ty(&self) -> Option { + self.syntax().children().find_map(Type::cast) + } +} diff --git a/crates/rue-parser/src/grammar.rs b/crates/rue-parser/src/grammar.rs index ddd155e..f8cda36 100644 --- a/crates/rue-parser/src/grammar.rs +++ b/crates/rue-parser/src/grammar.rs @@ -472,11 +472,19 @@ fn ty(p: &mut Parser) { return p.error(TYPE_RECOVERY_SET); } - while p.at(SyntaxKind::OpenBracket) { - p.start_at(checkpoint, SyntaxKind::ListType); - p.bump(); - p.expect(SyntaxKind::CloseBracket); - p.finish(); + loop { + if p.at(SyntaxKind::OpenBracket) { + p.start_at(checkpoint, SyntaxKind::ListType); + p.bump(); + p.expect(SyntaxKind::CloseBracket); + p.finish(); + } else if p.at(SyntaxKind::Question) { + p.start_at(checkpoint, SyntaxKind::OptionalType); + p.bump(); + p.finish(); + } else { + break; + } } } diff --git a/crates/rue-parser/src/parser.rs b/crates/rue-parser/src/parser.rs index b74763f..bbe70e6 100644 --- a/crates/rue-parser/src/parser.rs +++ b/crates/rue-parser/src/parser.rs @@ -222,6 +222,8 @@ fn convert_tokens<'a>( TokenKind::NotEquals => SyntaxKind::NotEquals, TokenKind::Assign => SyntaxKind::Assign, + TokenKind::Question => SyntaxKind::Question, + TokenKind::Whitespace => SyntaxKind::Whitespace, TokenKind::LineComment => SyntaxKind::LineComment, TokenKind::BlockComment { is_terminated } => { diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 7918dac..4cca5a1 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -46,6 +46,7 @@ pub enum SyntaxKind { Arrow, FatArrow, Spread, + Question, Plus, Minus, @@ -108,6 +109,7 @@ pub enum SyntaxKind { PairType, FunctionType, FunctionTypeParam, + OptionalType, } impl fmt::Display for SyntaxKind { @@ -155,6 +157,7 @@ impl fmt::Display for SyntaxKind { SyntaxKind::Arrow => "'->'", SyntaxKind::FatArrow => "'=>'", SyntaxKind::Spread => "'...'", + SyntaxKind::Question => "'?'", SyntaxKind::Plus => "'+'", SyntaxKind::Minus => "'-'", @@ -217,6 +220,7 @@ impl fmt::Display for SyntaxKind { SyntaxKind::PairType => "pair type", SyntaxKind::FunctionType => "function type", SyntaxKind::FunctionTypeParam => "function type parameter", + SyntaxKind::OptionalType => "optional type", } ) } diff --git a/examples/puzzles/non_standard_p2.rue b/examples/puzzles/non_standard_p2.rue new file mode 100644 index 0000000..417c804 --- /dev/null +++ b/examples/puzzles/non_standard_p2.rue @@ -0,0 +1,38 @@ +enum Condition { + AggSigMe = 50 { + public_key: PublicKey, + message: Bytes, + } +} + +fun tree_hash(value: Any) -> Bytes32 { + if value is Bytes { + sha256(1 as Bytes + value) + } else { + sha256(2 as Bytes + tree_hash(value.first) + tree_hash(value.rest)) + } +} + +fun main( + synthetic_pk: PublicKey, + original_pk: PublicKey?, + delegated_puzzle: fun(...Any) -> Condition[], + solution: Any +) -> Condition[] { + let conditions = delegated_puzzle(...solution); + let delegated_puzzle_hash = tree_hash(delegated_puzzle); + + if original_pk != nil { + let exponent = sha256(original_pk as Bytes + delegated_puzzle_hash); + let offset_pk = pubkey_for_exp(exponent); + assert synthetic_pk == original_pk + offset_pk; + return conditions; + } + + let agg_sig_me = Condition::AggSigMe { + public_key: synthetic_pk, + message: delegated_puzzle_hash, + }; + + [agg_sig_me, ...conditions] +} diff --git a/examples/signature_puzzle.rue b/examples/puzzles/signature_puzzle.rue similarity index 99% rename from examples/signature_puzzle.rue rename to examples/puzzles/signature_puzzle.rue index e788446..1b9420d 100644 --- a/examples/signature_puzzle.rue +++ b/examples/puzzles/signature_puzzle.rue @@ -20,3 +20,4 @@ fun tree_hash(value: Any) -> Bytes32 { sha256(2 as Bytes + tree_hash(value.first) + tree_hash(value.rest)) } } + diff --git a/tests.toml b/tests.toml index 24d7175..b1546fa 100644 --- a/tests.toml +++ b/tests.toml @@ -123,3 +123,10 @@ cost = 2716 input = "(50)" output = "\"Small\"" hash = "cf32bd32ff878903a6962acd218c451474e0ef3ae3c649f79aa5abca1bff730b" + +[non_standard_p2] +bytes = 401 +cost = 4755 +input = "(0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 () () ())" +output = "((g1_multiply 0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a))" +hash = "41ad4e730b68dcbd7cb2440343bc11c1b31eb5951273dac3ab93a00d99019ac6"