diff --git a/contrib/mir/src/ast/annotations.rs b/contrib/mir/src/ast/annotations.rs index 4a74c1de0bd5f88d930787ad0e483d69fc6b658f..623964267947d265b54cef2aefb7ecf73177ca9f 100644 --- a/contrib/mir/src/ast/annotations.rs +++ b/contrib/mir/src/ast/annotations.rs @@ -47,6 +47,14 @@ impl<'a> Annotations<'a> { self.0.is_empty() } + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } + pub fn get_single_field_ann(&self) -> Result>, AnnotationError> { use Annotation::*; let mut res = None; diff --git a/contrib/mir/src/ast/micheline.rs b/contrib/mir/src/ast/micheline.rs index 6d0c98f1b44320c9394f341dd21cea22beaceebe..1f562b39c16a72ba5c4ed8683adcf2b771a2dad9 100644 --- a/contrib/mir/src/ast/micheline.rs +++ b/contrib/mir/src/ast/micheline.rs @@ -59,6 +59,12 @@ impl<'a> From> for Micheline<'a> { } } +impl<'a> From<()> for Micheline<'a> { + fn from(_: ()) -> Self { + Micheline::App(Prim::Unit, &[], NO_ANNS) + } +} + impl<'a> From for Micheline<'a> { fn from(x: bool) -> Self { Micheline::prim0(if x { Prim::True } else { Prim::False }) @@ -83,6 +89,7 @@ macro_rules! micheline_types { | Prim::string | Prim::operation | Prim::unit + | Prim::never | Prim::address | Prim::chain_id | Prim::pair @@ -92,9 +99,23 @@ macro_rules! micheline_types { | Prim::contract | Prim::map | Prim::bytes + | Prim::bls12_381_g1 + | Prim::bls12_381_g2 + | Prim::bls12_381_fr + | Prim::ticket + | Prim::sapling_state + | Prim::sapling_transaction + | Prim::sapling_transaction_deprecated + | Prim::chest + | Prim::chest_key | Prim::key | Prim::key_hash - | Prim::signature, + | Prim::signature + | Prim::lambda + | Prim::timestamp + | Prim::tx_rollup_l2_address + | Prim::set + | Prim::big_map, .., ) }; @@ -112,7 +133,10 @@ macro_rules! micheline_literals { /// matches. macro_rules! micheline_fields { () => { - Micheline::App(Prim::parameter | Prim::storage | Prim::code, ..) + Micheline::App( + Prim::parameter | Prim::storage | Prim::code | Prim::view | Prim::constant, + .., + ) }; } @@ -124,9 +148,13 @@ macro_rules! micheline_instructions { Prim::PUSH | Prim::INT | Prim::GT + | Prim::GE | Prim::LE + | Prim::LT | Prim::EQ + | Prim::NEQ | Prim::LOOP + | Prim::LOOP_LEFT | Prim::DIP | Prim::ADD | Prim::DROP @@ -137,24 +165,94 @@ macro_rules! micheline_instructions { | Prim::FAILWITH | Prim::DUP | Prim::UNIT + | Prim::CAST + | Prim::RENAME + | Prim::ISNAT + | Prim::NAT + | Prim::BYTES | Prim::CAR | Prim::CDR | Prim::PAIR | Prim::SOME | Prim::COMPARE + | Prim::ADDRESS + | Prim::CONTRACT | Prim::AMOUNT | Prim::NIL + | Prim::MEM | Prim::GET | Prim::UPDATE + | Prim::GET_AND_UPDATE + | Prim::SIZE | Prim::UNPAIR + | Prim::NONE | Prim::CONS | Prim::ITER | Prim::CHAIN_ID - | Prim::SELF | Prim::SWAP + | Prim::SELF + | Prim::PACK + | Prim::UNPACK + | Prim::BLAKE2B + | Prim::KECCAK + | Prim::SHA256 + | Prim::SHA512 + | Prim::SHA3 + | Prim::PAIRING_CHECK + | Prim::OPEN_CHEST + | Prim::VIEW + | Prim::BALANCE + | Prim::NOW + | Prim::SOURCE + | Prim::SENDER + | Prim::SLICE + | Prim::TICKET_DEPRECATED + | Prim::TICKET + | Prim::READ_TICKET + | Prim::SPLIT_TICKET + | Prim::JOIN_TICKETS + | Prim::DIG + | Prim::DUG + | Prim::LEVEL + | Prim::SELF_ADDRESS + | Prim::NEVER + | Prim::STEPS_TO_QUOTA | Prim::CHECK_SIGNATURE + | Prim::CONCAT + | Prim::CREATE_ACCOUNT + | Prim::CREATE_CONTRACT + | Prim::IMPLICIT_ACCOUNT + | Prim::TRANSFER_TOKENS | Prim::SET_DELEGATE - | Prim::TRANSFER_TOKENS, + | Prim::EMIT + | Prim::HASH_KEY + | Prim::EMPTY_SET + | Prim::EMPTY_MAP + | Prim::EMPTY_BIG_MAP + | Prim::MIN_BLOCK_TIME + | Prim::VOTING_POWER + | Prim::TOTAL_VOTING_POWER + | Prim::SAPLING_EMPTY_STATE + | Prim::SAPLING_VERIFY_UPDATE + | Prim::ABS + | Prim::NEG + | Prim::SUB + | Prim::SUB_MUTEZ + | Prim::MUL + | Prim::EDIV + | Prim::LSL + | Prim::LSR + | Prim::EXEC + | Prim::APPLY + | Prim::LAMBDA + | Prim::LAMBDA_REC + | Prim::LEFT + | Prim::RIGHT + | Prim::MAP + | Prim::NOT + | Prim::AND + | Prim::XOR + | Prim::OR, .., ) }; @@ -174,6 +272,7 @@ macro_rules! micheline_values { | Prim::Elt | Prim::Left | Prim::Right + | Prim::Lambda_rec | Prim::Transfer_tokens | Prim::Set_delegate, .., diff --git a/contrib/mir/src/lexer.rs b/contrib/mir/src/lexer.rs index bb5b9c32e3477f8a2aa563534a32dd015d79359d..22f5ade39e2e2fda03c6ccf54e3260d3e57b4156 100644 --- a/contrib/mir/src/lexer.rs +++ b/contrib/mir/src/lexer.rs @@ -29,6 +29,7 @@ macro_rules! defprim { ($ty:ident; $($(#[token($str:expr)])? $prim:ident),* $(,)*) => { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(non_camel_case_types, clippy::upper_case_acronyms)] + #[repr(u8)] pub enum $ty { $($prim),* } @@ -57,75 +58,58 @@ macro_rules! defprim { } // NB: Primitives will be lexed as written, so capitalization matters. +// +// NB: Order matters too, it is used in binary serialization. +// For the correct order, see +// `src/proto_alpha/lib_protocol/michelson_v1_primitives.ml` file in this +// repository, `prim_encoding` function. +// TODO: https://gitlab.com/tezos/tezos/-/issues/6632 +// Add a test on ordering defprim! { Prim; - parameter, - storage, - code, - int, - nat, - bool, - mutez, - string, - unit, - operation, - pair, - option, - list, - map, - True, - False, - Unit, - None, - Pair, - Some, - Elt, - PUSH, - INT, - GT, - EQ, - LE, - LOOP, - DIP, - ADD, - DROP, - SWAP, - IF, - DUP, - FAILWITH, - UNIT, - CAR, - CDR, - PAIR, - IF_NONE, - SOME, - COMPARE, - AMOUNT, - NIL, - GET, - UPDATE, - UNPAIR, - CONS, - IF_CONS, - ITER, - or, - Left, - Right, - IF_LEFT, - contract, - address, - chain_id, - CHAIN_ID, - SELF, - bytes, - key, - signature, - CHECK_SIGNATURE, - key_hash, - TRANSFER_TOKENS, - SET_DELEGATE, - Transfer_tokens, - Set_delegate, + parameter, storage, code, False, Elt, Left, + None, Pair, Right, Some, True, Unit, + PACK, UNPACK, BLAKE2B, SHA256, SHA512, ABS, + ADD, AMOUNT, AND, BALANCE, CAR, CDR, + CHECK_SIGNATURE, COMPARE, CONCAT, CONS, + CREATE_ACCOUNT, CREATE_CONTRACT, IMPLICIT_ACCOUNT, DIP, + DROP, DUP, EDIV, EMPTY_MAP, EMPTY_SET, EQ, + EXEC, FAILWITH, GE, GET, GT, HASH_KEY, + IF, IF_CONS, IF_LEFT, IF_NONE, INT, LAMBDA, + LE, LEFT, LOOP, LSL, LSR, LT, MAP, + MEM, MUL, NEG, NEQ, NIL, NONE, NOT, + NOW, OR, PAIR, PUSH, RIGHT, SIZE, + SOME, SOURCE, SENDER, SELF, STEPS_TO_QUOTA, + SUB, SWAP, TRANSFER_TOKENS, SET_DELEGATE, UNIT, + UPDATE, XOR, ITER, LOOP_LEFT, ADDRESS, + CONTRACT, ISNAT, CAST, RENAME, bool, + contract, int, key, key_hash, lambda, list, + map, big_map, nat, option, or, pair, + set, signature, string, bytes, mutez, + timestamp, unit, operation, address, SLICE, + DIG, DUG, EMPTY_BIG_MAP, APPLY, chain_id, + CHAIN_ID, LEVEL, SELF_ADDRESS, never, NEVER, + UNPAIR, VOTING_POWER, TOTAL_VOTING_POWER, KECCAK, + SHA3, PAIRING_CHECK, bls12_381_g1, bls12_381_g2, + bls12_381_fr, sapling_state, sapling_transaction_deprecated, + SAPLING_EMPTY_STATE, SAPLING_VERIFY_UPDATE, ticket, + TICKET_DEPRECATED, READ_TICKET, SPLIT_TICKET, + JOIN_TICKETS, GET_AND_UPDATE, chest, chest_key, + OPEN_CHEST, VIEW, view, constant, SUB_MUTEZ, + tx_rollup_l2_address, MIN_BLOCK_TIME, sapling_transaction, + EMIT, Lambda_rec, LAMBDA_REC, TICKET, BYTES, + NAT, + // Unstable primitives (they are not part of a released protocol) + Transfer_tokens, Set_delegate + // If you add anything here, see the note about the order above. +} + +impl Prim { + // Our [Prim] enum has its variants in the right order, so its + // discriminant should match the ID. + pub fn encode(&self, out: &mut Vec) { + out.push(*self as u8) + } } defprim! { diff --git a/contrib/mir/src/lib.rs b/contrib/mir/src/lib.rs index 1d25bb433a0deeabbead713c046e635484b2e5c5..166108c7231c6ebaf30fa6042a77fd9c8b882de0 100644 --- a/contrib/mir/src/lib.rs +++ b/contrib/mir/src/lib.rs @@ -13,6 +13,7 @@ pub mod interpreter; pub mod irrefutable_match; pub mod lexer; pub mod parser; +pub mod serializer; pub mod stack; pub mod syntax; pub mod typechecker; diff --git a/contrib/mir/src/serializer.rs b/contrib/mir/src/serializer.rs new file mode 100644 index 0000000000000000000000000000000000000000..7087d404217bf42626bf95a3399f2fac47a8c28c --- /dev/null +++ b/contrib/mir/src/serializer.rs @@ -0,0 +1 @@ +mod encode; diff --git a/contrib/mir/src/serializer/encode.rs b/contrib/mir/src/serializer/encode.rs new file mode 100644 index 0000000000000000000000000000000000000000..b68f66d1ef217d5b67113627c1bc6ba6cad04f14 --- /dev/null +++ b/contrib/mir/src/serializer/encode.rs @@ -0,0 +1,321 @@ +/******************************************************************************/ +/* */ +/* SPDX-License-Identifier: MIT */ +/* Copyright (c) [2023] Serokell */ +/* */ +/******************************************************************************/ + +//! Micheline serialization. + +use std::mem::size_of; + +use crate::{ + ast::{annotations::Annotations, Micheline}, + lexer::{Annotation, Prim}, +}; + +/// Prefix denoting an encoded string. +const STRING_TAG: u8 = 0x01; +/// Prefix denoting an encoded sequence. +const SEQ_TAG: u8 = 0x02; +/// Prefix denoting an encoded bytes sequence. +const BYTES_TAG: u8 = 0x0a; + +trait AppEncoder<'a>: IntoIterator> + Sized { + const NO_ANNOTS_TAG: u8; + const WITH_ANNOTS_TAG: u8; + fn encode(prim: &Prim, args: Self, annots: &Annotations, out: &mut Vec) { + if annots.is_empty() { + out.push(Self::NO_ANNOTS_TAG); + } else { + out.push(Self::WITH_ANNOTS_TAG); + } + prim.encode(out); + for arg in args { + encode_micheline(arg, out) + } + if !annots.is_empty() { + annots.encode_bytes(out) + } + } +} + +impl<'a> AppEncoder<'a> for [&'a Micheline<'a>; 0] { + const NO_ANNOTS_TAG: u8 = 0x03; + const WITH_ANNOTS_TAG: u8 = 0x04; +} + +impl<'a> AppEncoder<'a> for [&'a Micheline<'a>; 1] { + const NO_ANNOTS_TAG: u8 = 0x05; + const WITH_ANNOTS_TAG: u8 = 0x06; +} + +impl<'a> AppEncoder<'a> for [&'a Micheline<'a>; 2] { + const NO_ANNOTS_TAG: u8 = 0x07; + const WITH_ANNOTS_TAG: u8 = 0x08; +} + +impl<'a> AppEncoder<'a> for &'a [Micheline<'a>] { + const NO_ANNOTS_TAG: u8 = 0x09; + const WITH_ANNOTS_TAG: u8 = 0x09; + fn encode(prim: &Prim, args: Self, annots: &Annotations, out: &mut Vec) { + match args { + [] => AppEncoder::encode(prim, [], annots, out), + [arg] => AppEncoder::encode(prim, [arg], annots, out), + [arg1, arg2] => AppEncoder::encode(prim, [arg1, arg2], annots, out), + _ => { + out.push(Self::WITH_ANNOTS_TAG); + prim.encode(out); + with_patchback_len(out, |out| { + for arg in args { + encode_micheline(arg, out) + } + }); + annots.encode_bytes(out) + } + } + } +} + +impl Annotation<'_> { + pub fn encode_bytes(&self, out: &mut Vec) { + match self { + Annotation::Special(s) => out.extend_from_slice(s.as_bytes()), + Annotation::Field(s) => { + out.push(b'%'); + out.extend_from_slice(s.as_bytes()); + } + Annotation::Variable(s) => { + out.push(b'@'); + out.extend_from_slice(s.as_bytes()); + } + Annotation::Type(s) => { + out.push(b':'); + out.extend_from_slice(s.as_bytes()); + } + } + } +} + +impl Annotations<'_> { + pub fn encode_bytes(&self, out: &mut Vec) { + with_patchback_len(out, |out| { + // Add them space-separated + let mut is_first = true; + for ann in self.iter() { + if !is_first { + out.push(b' ') + } + is_first = false; + ann.encode_bytes(out); + } + }) + } +} + +/// Length of some container, usually stored as fixed-length number. +type Len = u32; + +/// Put length of something. +fn put_len(len: Len, out: &mut Vec) { + out.extend_from_slice(&len.to_be_bytes()) +} + +/// Put bytestring (with its length). +fn put_bytes(bs: &[u8], out: &mut Vec) { + out.push(BYTES_TAG); + put_len(bs.len() as Len, out); + out.extend_from_slice(bs) +} + +/// Put a Michelson string. +fn put_string(s: &str, out: &mut Vec) { + out.push(STRING_TAG); + put_len(s.len() as Len, out); + out.extend_from_slice(s.as_bytes()) +} + +fn with_patchback_len(out: &mut Vec, f: impl FnOnce(&mut Vec)) { + put_len(0, out); // don't know the right length in advance + let i = out.len(); + let len_place = (i - size_of::())..i; // to fill length later + f(out); + let len_of_written = (out.len() - i) as Len; + out[len_place].copy_from_slice(&len_of_written.to_be_bytes()) +} + +/// Put a container. +fn put_seq(list: &[V], out: &mut Vec, encoder: fn(&V, &mut Vec)) { + out.push(SEQ_TAG); + with_patchback_len(out, |out| { + for val in list { + encoder(val, out) + } + }); +} + +/// Recursive encoding function for [Value]. +fn encode_micheline(mich: &Micheline, out: &mut Vec) { + use Micheline::*; + match mich { + Int(_) => todo!(), // for a later MR + String(s) => put_string(s, out), + Bytes(b) => put_bytes(b, out), + Seq(s) => put_seq(s, out, encode_micheline), + App(prim, args, anns) => AppEncoder::encode(prim, *args, anns, out), + } +} + +impl<'a> Micheline<'a> { + /// Serialize value. + #[allow(dead_code)] // Until we add PACK + fn encode(&self) -> Vec { + self.encode_starting_with(&[]) + } + + /// Like [Value::encode], but allows specifying a prefix, useful for + /// `PACK` implementation. + fn encode_starting_with(&self, start_bytes: &[u8]) -> Vec { + let mut out = Vec::from(start_bytes); + encode_micheline(self, &mut out); + out + } +} + +#[cfg(test)] +mod test_encoding { + use super::*; + + #[track_caller] + fn check<'a>(v: impl Into>, hex_bytes: &str) { + let hex_bytes: &str = hex_bytes + .strip_prefix("0x") + .unwrap_or_else(|| panic!("The `expected` argument must start from 0x")); + assert_eq!( + v.into().encode(), + hex::decode(hex_bytes) + .unwrap_or_else(|_| panic!("Bad hex string in `expected` argument")) + ) + } + // To figure out the expected bytes, use + // octez-client convert data 'VALUE' from michelson to binary + + mod value { + use crate::ast::micheline::test_helpers::{app, seq}; + + use super::*; + + #[test] + fn primitive_values() { + check((), "0x030b"); + check(true, "0x030a"); + check(false, "0x0303"); + } + + #[test] + fn simple_nested() { + check(app!(Pair[true, ""]), "0x0707030a0100000000"); + check(app!(None[]), "0x0306"); + check(app!(Some[app!(Unit)]), "0x0509030b"); + check(app!(Elt[true, ()]), "0x0704030a030b"); + check( + seq! { app!(DROP); app!(LAMBDA[app!(unit), app!(unit), seq!{}]) }, + "0x02000000150320093100000009036c036c020000000000000000", + ); + } + + #[test] + fn string() { + check("", "0x0100000000"); + check("abc", "0x0100000003616263"); + check( + "123456789123456789123456789", + "0x010000001b313233343536373839313233343536373839313233343536373839", + ); + } + + #[test] + fn very_long_string() { + // Using "\"$(printf 'x%.0s' {1..1000})\"" as a value + // Verifies that length is encoded as a fixed-length number, not as zarith + check( + "x".repeat(1000), + "0x01000003e878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878" + ); + } + + #[test] + fn bytes() { + check(hex::decode("").unwrap(), "0x0a00000000"); + check(hex::decode("001234abff").unwrap(), "0x0a00000005001234abff"); + } + + #[test] + fn list() { + check(seq! {}, "0x0200000000"); + check(seq! {true; false}, "0x0200000004030a0303"); + } + + #[test] + fn deeply_nested_list() { + check( + seq! {seq!{}; seq!{true}}, + "0x020000000c02000000000200000002030a", + ); + } + + #[test] + fn very_long_list() { + // Using "{ $(printf 'Unit;%.0s' {1..1000}) }" as a value + // Verifies that length is encoded as a fixed-length number, not as zarith + check( + Micheline::Seq(&vec![app!(Unit); 1000]), + "0x02000007d0030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b030b", + ); + } + } + + mod annotations { + use crate::parser::test_helpers::*; + + use super::*; + + #[test] + fn trivial() { + check(parse("(int %a)").unwrap(), "0x045b000000022561"); + check(parse("(int :a)").unwrap(), "0x045b000000023a61"); + check( + parse("(int @abc123)").unwrap(), + "0x045b0000000740616263313233", + ); + } + + #[test] + fn several_annotations() { + check( + parse("(int %a :b @c %d)").unwrap(), + "0x045b0000000b2561203a62204063202564", + ); + } + + #[test] + fn nested_entries() { + check( + parse("(pair %a (int %b))").unwrap(), + "0x0665045b000000022562000000022561", + ); + } + + #[test] + fn generic_case() { + check( + parse("LAMBDA (int %a) (int :b) {}").unwrap(), + "0x093100000015045b000000022561045b000000023a62020000000000000000", + ); + check( + parse("LAMBDA (int %a %b %c %d) int {}").unwrap(), + "0x093100000018045b0000000b2561202562202563202564035b020000000000000000", + ); + } + } +} diff --git a/contrib/mir/src/typechecker.rs b/contrib/mir/src/typechecker.rs index ea5ef5f13db0416e75cfeefa4394e2db2bfb5aef..c27a54fe8576a21845df30282af639fa6574f9bb 100644 --- a/contrib/mir/src/typechecker.rs +++ b/contrib/mir/src/typechecker.rs @@ -366,6 +366,8 @@ fn parse_ty_with_entrypoints( | micheline_instructions!() | micheline_literals!() | micheline_values!() => unexpected()?, + + App(other, ..) => todo!("Unhandled type {other}"), }; if let Option::Some(eps) = entrypoints { // we just ensured it's an application of some type primitive @@ -981,6 +983,8 @@ pub(crate) fn typecheck_instruction( (App(CHECK_SIGNATURE, [], _), [] | [_] | [_, _]) => no_overload!(CHECK_SIGNATURE, len 3), (App(CHECK_SIGNATURE, expect_args!(0), _), _) => unexpected_micheline!(), + (App(other, ..), _) => todo!("Unhandled instruction {other}"), + (Seq(nested), _) => I::Seq(typecheck(nested, ctx, self_entrypoints, opt_stack)?), }) }