Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Introduce the signature type and signature::verify. #2519

Merged
merged 12 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
726 changes: 407 additions & 319 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ members = [
]

[workspace.dependencies.snarkvm]
version = "=0.14.5"
git = "https://github.com/AleoHQ/snarkVM.git"
branch = "testnet3"

[workspace.dependencies.snarkvm-console]
version = "=0.14.5"
git = "https://github.com/AleoHQ/snarkVM.git"
branch = "testnet3"

[lib]
path = "leo/lib.rs"
Expand Down
9 changes: 8 additions & 1 deletion compiler/ast/src/functions/core_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ pub enum CoreFunction {

GroupToXCoordinate,
GroupToYCoordinate,

SignatureVerify,
}

impl CoreFunction {
Expand Down Expand Up @@ -380,6 +382,8 @@ impl CoreFunction {

(sym::group, sym::to_x_coordinate) => Self::GroupToXCoordinate,
(sym::group, sym::to_y_coordinate) => Self::GroupToYCoordinate,

(sym::signature, sym::verify) => Self::SignatureVerify,
_ => return None,
})
}
Expand Down Expand Up @@ -565,6 +569,8 @@ impl CoreFunction {

Self::GroupToXCoordinate => 1,
Self::GroupToYCoordinate => 1,

Self::SignatureVerify => 3,
}
}

Expand Down Expand Up @@ -736,7 +742,8 @@ impl CoreFunction {
| CoreFunction::Poseidon8HashToU128
| CoreFunction::Poseidon8HashToScalar
| CoreFunction::GroupToXCoordinate
| CoreFunction::GroupToYCoordinate => false,
| CoreFunction::GroupToYCoordinate
| CoreFunction::SignatureVerify => false,
}
}
}
4 changes: 4 additions & 0 deletions compiler/ast/src/types/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub enum Type {
Mapping(MappingType),
/// The `scalar` type.
Scalar,
/// The `signature` type.
Signature,
/// The `string` type.
String,
/// A static tuple of at least one type.
Expand All @@ -64,6 +66,7 @@ impl Type {
| (Type::Field, Type::Field)
| (Type::Group, Type::Group)
| (Type::Scalar, Type::Scalar)
| (Type::Signature, Type::Signature)
| (Type::String, Type::String)
| (Type::Unit, Type::Unit) => true,
(Type::Integer(left), Type::Integer(right)) => left.eq(right),
Expand All @@ -90,6 +93,7 @@ impl fmt::Display for Type {
Type::Integer(ref integer_type) => write!(f, "{integer_type}"),
Type::Mapping(ref mapping_type) => write!(f, "{mapping_type}"),
Type::Scalar => write!(f, "scalar"),
Type::Signature => write!(f, "signature"),
Type::String => write!(f, "string"),
Type::Tuple(ref tuple) => write!(f, "{tuple}"),
Type::Unit => write!(f, "()"),
Expand Down
14 changes: 14 additions & 0 deletions compiler/parser/src/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,20 @@ impl ParserContext<'_> {
right: Box::new(args.swap_remove(0)),
id: self.node_builder.next_id(),
}))
} else if let (2, Some(CoreFunction::SignatureVerify)) =
(args.len(), CoreFunction::from_symbols(sym::signature, method.name))
{
Ok(Expression::Access(AccessExpression::AssociatedFunction(AssociatedFunction {
ty: Type::Identifier(Identifier::new(sym::signature, self.node_builder.next_id())),
name: method,
arguments: {
let mut arguments = vec![receiver];
arguments.extend(args);
arguments
},
span,
id: self.node_builder.next_id(),
})))
} else {
// Attempt to parse the method call as a mapping operation.
match (args.len(), CoreFunction::from_symbols(sym::Mapping, method.name)) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/parser/src/parser/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(super) const TYPE_TOKENS: &[Token] = &[
Token::Field,
Token::Group,
Token::Scalar,
Token::Signature,
Token::String,
Token::I8,
Token::I16,
Expand Down Expand Up @@ -66,6 +67,7 @@ impl ParserContext<'_> {
Token::Field => Type::Field,
Token::Group => Type::Group,
Token::Scalar => Type::Scalar,
Token::Signature => Type::Signature,
Token::String => Type::String,
x => Type::Integer(Self::token_to_int_type(x).expect("invalid int type")),
},
Expand Down
1 change: 1 addition & 0 deletions compiler/parser/src/tokenizer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ impl Token {
"record" => Token::Record,
"return" => Token::Return,
"scalar" => Token::Scalar,
"signature" => Token::Signature,
"self" => Token::SelfLower,
"string" => Token::String,
"struct" => Token::Struct,
Expand Down
3 changes: 2 additions & 1 deletion compiler/parser/src/tokenizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ mod tests {
return
scalar
self
signature
string
struct
test
Expand Down Expand Up @@ -169,7 +170,7 @@ mod tests {

assert_eq!(
output,
r#""test" "test{}test" "test{}" "{}test" "test{" "test}" "test{test" "test}test" "te{{}}" test_ident 12345 address as assert assert_eq assert_neq async bool const else false field finalize for function group i128 i64 i32 i16 i8 if in inline input let mut private program public return scalar self string struct test then transition true u128 u64 u32 u16 u8 console ! != && ( ) * ** + , - -> => _ . .. / : ; < <= = == > >= [ ] { { } } || ? @ // test
r#""test" "test{}test" "test{}" "{}test" "test{" "test}" "test{test" "test}test" "te{{}}" test_ident 12345 address as assert assert_eq assert_neq async bool const else false field finalize for function group i128 i64 i32 i16 i8 if in inline input let mut private program public return scalar self signature string struct test then transition true u128 u64 u32 u16 u8 console ! != && ( ) * ** + , - -> => _ . .. / : ; < <= = == > >= [ ] { { } } || ? @ // test
/* test */ // "#
);
});
Expand Down
4 changes: 4 additions & 0 deletions compiler/parser/src/tokenizer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub enum Token {
Field,
Group,
Scalar,
Signature,
String,
I8,
I16,
Expand Down Expand Up @@ -178,6 +179,7 @@ pub const KEYWORD_TOKENS: &[Token] = &[
Token::Record,
Token::Return,
Token::SelfLower,
Token::Signature,
Token::Scalar,
Token::String,
Token::Struct,
Expand Down Expand Up @@ -234,6 +236,7 @@ impl Token {
Token::Record => sym::record,
Token::Return => sym::Return,
Token::Scalar => sym::scalar,
Token::Signature => sym::signature,
Token::SelfLower => sym::SelfLower,
Token::String => sym::string,
Token::Struct => sym::Struct,
Expand Down Expand Up @@ -321,6 +324,7 @@ impl fmt::Display for Token {
Field => write!(f, "field"),
Group => write!(f, "group"),
Scalar => write!(f, "scalar"),
Signature => write!(f, "signature"),
String => write!(f, "string"),
I8 => write!(f, "i8"),
I16 => write!(f, "i16"),
Expand Down
12 changes: 12 additions & 0 deletions compiler/passes/src/code_generation/visit_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,18 @@ impl<'a> CodeGenerator<'a> {
.expect("failed to write to string");
(destination_register, instruction)
}
Type::Identifier(Identifier { name: sym::signature, .. }) => {
let mut instruction = " sign.verify".to_string();
let destination_register = get_destination_register();
// Write the arguments and the destination register.
writeln!(
instruction,
" {} {} {} into {destination_register};",
arguments[0], arguments[1], arguments[2]
)
.expect("failed to write to string");
(destination_register, instruction)
}
_ => unreachable!("All core functions should be known at this phase of compilation"),
};
// Add the instruction to the list of instructions.
Expand Down
1 change: 1 addition & 0 deletions compiler/passes/src/code_generation/visit_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl<'a> CodeGenerator<'a> {
| Type::Field
| Type::Group
| Type::Scalar
| Type::Signature
| Type::String
| Type::Integer(..) => format!("{input}"),
Type::Identifier(ident) => format!("{ident}"),
Expand Down
21 changes: 20 additions & 1 deletion compiler/passes/src/type_checking/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ const GROUP_TYPE: Type = Type::Group;

const SCALAR_TYPE: Type = Type::Scalar;

const SIGNATURE_TYPE: Type = Type::Signature;

const INT_TYPES: [Type; 10] = [
Type::Integer(IntegerType::I8),
Type::Integer(IntegerType::I16),
Expand Down Expand Up @@ -173,7 +175,6 @@ impl<'a> TypeChecker<'a> {
self.emit_err(TypeCheckerError::type_should_be(actual.clone(), expected, span));
}
}

actual
}

Expand All @@ -182,6 +183,11 @@ impl<'a> TypeChecker<'a> {
self.check_type(|actual: &Type| actual.eq_flat(expected), expected.to_string(), actual, span)
}

/// Emits an error to the error handler if the given type is not an address.
pub(crate) fn assert_address_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| ADDRESS_TYPE.eq(type_), ADDRESS_TYPE.to_string(), type_, span)
}

/// Emits an error to the handler if the given type is not a boolean.
pub(crate) fn assert_bool_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| BOOLEAN_TYPE.eq(type_), BOOLEAN_TYPE.to_string(), type_, span)
Expand All @@ -202,6 +208,11 @@ impl<'a> TypeChecker<'a> {
self.check_type(|type_: &Type| SCALAR_TYPE.eq(type_), SCALAR_TYPE.to_string(), type_, span)
}

/// Emits an error to the handler if the given type is not a signature.
pub(crate) fn assert_signature_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| SIGNATURE_TYPE.eq(type_), SIGNATURE_TYPE.to_string(), type_, span)
}

/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_: &Type| INT_TYPES.contains(type_), types_to_string(&INT_TYPES), type_, span)
Expand Down Expand Up @@ -917,6 +928,14 @@ impl<'a> TypeChecker<'a> {
CoreFunction::ChaChaRandU32 => Some(Type::Integer(IntegerType::U32)),
CoreFunction::ChaChaRandU64 => Some(Type::Integer(IntegerType::U64)),
CoreFunction::ChaChaRandU128 => Some(Type::Integer(IntegerType::U128)),
CoreFunction::SignatureVerify => {
// Check that the first argument is a signature.
self.assert_signature_type(&arguments[0].0, arguments[0].1);
// Check that the second argument is an address.
self.assert_address_type(&arguments[1].0, arguments[1].1);
// Return a boolean.
Some(Type::Boolean)
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ symbols! {
set,
to_x_coordinate,
to_y_coordinate,
verify,

// types
address,
Expand All @@ -208,6 +209,7 @@ symbols! {
i128,
record,
scalar,
signature,
string,
Struct: "struct",
u8,
Expand Down
12 changes: 12 additions & 0 deletions tests/expectations/compiler/signature/signature.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
namespace: Compile
expectation: Pass
outputs:
- - initial_ast: 66326647c8591d4479e5622bc50d2fffb7fcc9791925a7ab7018617a59b693a4
unrolled_ast: 66326647c8591d4479e5622bc50d2fffb7fcc9791925a7ab7018617a59b693a4
ssa_ast: 76f4f34b1e014012ba3ecc136b58787ed41de71f5ff83565f825258b9e8ea76f
flattened_ast: b0658ac6a7f5033380851c648f130884de04fde4bef415fa015e4d664aabb427
inlined_ast: b0658ac6a7f5033380851c648f130884de04fde4bef415fa015e4d664aabb427
dce_ast: b0658ac6a7f5033380851c648f130884de04fde4bef415fa015e4d664aabb427
bytecode: 87b34bffefde3c2b9e23ce08528737a4ef06a28ee9120c02d26ee41989f24cd2
warnings: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372007]: Expected one type from `signature`, but got `address`\n --> compiler-test:11:45\n |\n 11 | let first: bool = signature::verify(a, s, v);\n | ^\nError [ETYC0372007]: Expected one type from `address`, but got `signature`\n --> compiler-test:11:48\n |\n 11 | let first: bool = signature::verify(a, s, v);\n | ^\nError [ETYC0372007]: Expected one type from `address`, but got `u8`\n --> compiler-test:12:37\n |\n 12 | let second: bool = s.verify(1u8, v);\n | ^^^\nError [ETYC0372007]: Expected one type from `signature`, but got `address`\n --> compiler-test:17:45\n |\n 17 | let first: bool = signature::verify(a, v, s);\n | ^\nError [ETYC0372007]: Expected one type from `address`, but got `foo`\n --> compiler-test:17:48\n |\n 17 | let first: bool = signature::verify(a, v, s);\n | ^\nError [ETYC0372007]: Expected one type from `address`, but got `foo`\n --> compiler-test:18:37\n |\n 18 | let second: bool = s.verify(v, a);\n | ^\n"
4 changes: 2 additions & 2 deletions tests/expectations/parser/expression/cast_fail.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace: ParseExpression
expectation: Fail
outputs:
- "did not consume all input: 'aas' @ 1:5-8\n'u8' @ 1:9-11\n"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '{'\n --> test:1:10\n |\n 1 | 1u128 as { foo: u8 }\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '{'\n --> test:1:10\n |\n 1 | 1u128 as { foo: u8 }\n | ^"
- "did not consume all input: ';' @ 1:14-15\n"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found 'bar'\n --> test:1:8\n |\n 1 | 1u8 as bar;\n | ^^^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found 'bar'\n --> test:1:8\n |\n 1 | 1u8 as bar;\n | ^^^"
- "did not consume all input: 'asu8' @ 1:5-9\n"
14 changes: 7 additions & 7 deletions tests/expectations/parser/statement/definition_fail.out
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ outputs:
- "Error [EPAR0370009]: unexpected string: expected 'expression', found ','\n --> test:1:10\n |\n 1 | let (x,y,,) = ();\n | ^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found ','\n --> test:1:6\n |\n 1 | let (,x,y) = ();\n | ^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found ','\n --> test:1:8\n |\n 1 | let (x,,y) = ();\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8; (2,,)] = [[0,0], [0,0]];\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found 'constant'\n --> test:1:8\n |\n 1 | let x: constant = expr;\n | ^^^^^^^^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8; (2,,)] = [[0,0], [0,0]];\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found 'constant'\n --> test:1:8\n |\n 1 | let x: constant = expr;\n | ^^^^^^^^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found 'constant'\n --> test:1:1\n |\n 1 | constant x: let = expr;\n | ^^^^^^^^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found '<eof>'\n --> test:1:1\n |\n 1 | let\n | ^^^"
- "Error [EPAR0370005]: expected : -- found '<eof>'\n --> test:1:5\n |\n 1 | let x\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '<eof>'\n --> test:1:6\n |\n 1 | let x:\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '<eof>'\n --> test:1:6\n |\n 1 | let x:\n | ^"
- "Error [EPAR0370005]: expected : -- found '='\n --> test:1:7\n |\n 1 | let x = (a, y]);\n | ^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found '='\n --> test:1:5\n |\n 1 | let = 1u8;\n | ^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found ';'\n --> test:1:4\n |\n 1 | let;\n | ^"
- "Error [EPAR0370005]: expected : -- found '1'\n --> test:1:7\n |\n 1 | let x 1u8;\n | ^"
- "Error [EPAR0370005]: expected = -- found ';'\n --> test:1:10\n |\n 1 | let x: u8;\n | ^"
- "Error [EPAR0370005]: expected = -- found '<eof>'\n --> test:1:8\n |\n 1 | let x: u8\n | ^^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '='\n --> test:1:8\n |\n 1 | let x: = 1;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8] = 1;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8; 1u8] = [1,\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '='\n --> test:1:8\n |\n 1 | let x: = 1;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8] = 1;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8;\n | ^"
- "Error [EPAR0370005]: expected 'address', 'bool', 'field', 'group', 'scalar', 'signature', 'string', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128' -- found '['\n --> test:1:8\n |\n 1 | let x: [u8; 1u8] = [1,\n | ^"
- "Error [EPAR0370009]: unexpected string: expected 'expression', found ']'\n --> test:1:15\n |\n 1 | let dbg: u8 = ];\n | ^"
- "Error [EPAR0370016]: Could not lex the following content: `🦀:`.\n"
- "Error [EPAR0370005]: expected : -- found '='\n --> test:1:9\n |\n 1 | let (x) = ...;\n | ^"
Expand Down
Loading
Loading