From 4b3d94a561bfe3aa389b2e323026840063c24d94 Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Fri, 28 Jul 2023 01:18:47 +0300 Subject: [PATCH] Implement all instructions, add DUP check --- michelson_vm/src/instructions/mod.rs | 2 +- michelson_vm/src/instructions/stack.rs | 5 + michelson_vm/src/instructions/tickets.rs | 145 ++++++++++++++--------- michelson_vm/src/interpreter.rs | 4 + michelson_vm/src/types.rs | 9 +- michelson_vm/src/types/nat.rs | 6 +- michelson_vm/src/types/ticket.rs | 30 +++-- 7 files changed, 121 insertions(+), 80 deletions(-) diff --git a/michelson_vm/src/instructions/mod.rs b/michelson_vm/src/instructions/mod.rs index 6ddd4fb..df84dee 100644 --- a/michelson_vm/src/instructions/mod.rs +++ b/michelson_vm/src/instructions/mod.rs @@ -12,4 +12,4 @@ mod lambda; mod math; mod scope; mod stack; -mod tickets; \ No newline at end of file +mod tickets; diff --git a/michelson_vm/src/instructions/stack.rs b/michelson_vm/src/instructions/stack.rs index 51bb766..2e09bd5 100644 --- a/michelson_vm/src/instructions/stack.rs +++ b/michelson_vm/src/instructions/stack.rs @@ -42,6 +42,11 @@ impl PureInterpreter for Dup { } // TODO: check if copyable let res = stack.dup_at(n - 1)?; + + if let StackItem::Ticket(_) = res { + return err_unsupported!("TICKETS DUP"); + } + stack.push(res) } } diff --git a/michelson_vm/src/instructions/tickets.rs b/michelson_vm/src/instructions/tickets.rs index 105020e..c1a0def 100644 --- a/michelson_vm/src/instructions/tickets.rs +++ b/michelson_vm/src/instructions/tickets.rs @@ -2,88 +2,119 @@ // // SPDX-License-Identifier: MIT -use tezos_michelson::michelson::data::instructions::{ - Ticket, ReadTicket, SplitTicket, JoinTickets, +use tezos_michelson::michelson::{ + data::instructions::{JoinTickets, ReadTicket, SplitTicket, Ticket}, + types, }; use crate::{ err_mismatch, - interpreter::{ - ContextInterpreter, InterpreterContext, PureInterpreter, - }, - //pop_cast, + interpreter::{PureInterpreter, ScopedInterpreter}, + pop_cast, stack::Stack, - Result, types::{AddressItem, NatItem, StackItem, TicketItem, PairItem, OptionItem}, + typechecker::check_type_comparable, + types::{AddressItem, OptionItem, PairItem, StackItem, TicketItem}, + OperationScope, Result, }; -impl ContextInterpreter for Ticket { - fn execute(&self, stack: &mut Stack, context: &mut impl InterpreterContext) -> Result<()> { +impl ScopedInterpreter for Ticket { + fn execute(&self, stack: &mut Stack, scope: &OperationScope) -> Result<()> { let identifier = stack.pop()?; - let amount = stack.pop()?; - - // TODO: compare amount with zero - // TODO: convert StackItem identifier to Micheline - // TODO: get Type for identifier - // TODO: get self address - // TODO: save balance info to context? - - //stack.push(StackItem::Ticket(TicketItem::new())); - Ok(()) + let identifier_ty = identifier.get_type()?; + check_type_comparable(&identifier_ty)?; + + let amount = pop_cast!(stack, Nat); + + if amount.is_zero() { + let ty = types::ticket(identifier_ty); + return stack.push(StackItem::Option(OptionItem::None(ty))); + } + + let ticket = TicketItem { + source: AddressItem::new(scope.self_address.clone().into()), + identifier: Box::new(identifier), + amount: amount, + }; + + stack.push(StackItem::Option(OptionItem::Some(Box::new(ticket.into())))) } } impl PureInterpreter for ReadTicket { fn execute(&self, stack: &mut Stack) -> Result<()> { - let ticket_item = stack.pop()?; - let ticket = match ticket_item { - StackItem::Ticket(ticket) => ticket, - item => return err_mismatch!("Ticket", item) - }; + let ticket = pop_cast!(stack, Ticket); - let source = StackItem::Address(AddressItem::new(ticket.source)); - let identifier = ticket.identifier; // TODO: identifier to StackItem - let amount = StackItem::Nat(NatItem::new(ticket.amount)); - - let pair = PairItem::from_items(vec![source, identifier, amount])?; + let pair = PairItem::from_items(vec![ + ticket.source.clone().into(), + *ticket.identifier.clone(), + ticket.amount.clone().into(), + ])?; - stack.push(StackItem::Pair(pair)); - stack.push(ticket_item); // return ticket back to stack - Ok(()) + stack.push(ticket.into())?; // return ticket back to stack + stack.push(pair.into()) } } -impl ContextInterpreter for SplitTicket { - fn execute(&self, stack: &mut Stack, context: &mut impl InterpreterContext) -> Result<()> { - let ticket = stack.pop()?; // ticket - let split_pair = stack.pop()?; // pair nat nat +impl PureInterpreter for SplitTicket { + fn execute(&self, stack: &mut Stack) -> Result<()> { + let ticket = pop_cast!(stack, Ticket); // ticket + let pair_n1_n2 = pop_cast!(stack, Pair); // pair nat nat - // TODO: if n + m != ticket.amount or n == 0 or m == 0 return none - stack.push(StackItem::Option(OptionItem::None())); + let (n1, n2) = match pair_n1_n2.unpair() { + (StackItem::Nat(n1), StackItem::Nat(n2)) => (n1, n2), + (s1, s2) => { + return err_mismatch!( + "Pair Nat Nat", + StackItem::Pair(PairItem::new(s1, s2)) + ) + } + }; - // TODO: else return pair (ticket_n, ticket_m) - stack.push(StackItem::Option(OptionItem::Some())); - - // TODO: update balance in context? + if n1.is_zero() || n2.is_zero() || n1.clone() + n2.clone() != ticket.amount { + let ty = types::pair(vec![types::nat(), types::nat()]); + return stack.push(StackItem::Option(OptionItem::None(ty))); + } - Ok(()) + let ticket_1 = TicketItem { + source: ticket.source.clone(), + identifier: ticket.identifier.clone(), + amount: n1, + }; + let ticket_2 = TicketItem { + source: ticket.source, + identifier: ticket.identifier, + amount: n2, + }; + let pair = PairItem::new(ticket_1.into(), ticket_2.into()); + + stack.push(StackItem::Option(OptionItem::Some(Box::new(pair.into())))) } } -impl ContextInterpreter for JoinTickets { - fn execute(&self, stack: &mut Stack, context: &mut impl InterpreterContext) -> Result<()> { - let tickets = stack.pop()?; // tickets pair - // TODO: get ticket_a - // TODO: get ticket_b - // TODO: compare sources and identifiers (and identifiers types?) +impl PureInterpreter for JoinTickets { + fn execute(&self, stack: &mut Stack) -> Result<()> { + let tickets = pop_cast!(stack, Pair); // tickets pair + let (ticket_1, ticket_2) = match tickets.unpair() { + (StackItem::Ticket(ticket_1), StackItem::Ticket(ticket_2)) => (ticket_1, ticket_2), + (s1, s2) => { + return err_mismatch!( + "Pair Ticket Ticket", + StackItem::Pair(PairItem::new(s1, s2)) + ) + } + }; - // TODO: if ticket_a.source != ticket_b.source or ticket_a.identifier != ticket_b.identifier - stack.push(StackItem::Option(OptionItem::None())); + if ticket_1.source != ticket_2.source || *ticket_1.identifier != *ticket_2.identifier { + let ty = types::ticket(ticket_1.identifier.get_type()?); + return stack.push(StackItem::Option(OptionItem::None(ty))); + } - // TODO: OR otherwise return Some(ticket) - stack.push(StackItem::Option(OptionItem::Some())); - - // TODO: update balance in context? + let ticket = TicketItem { + source: ticket_1.source, + identifier: ticket_1.identifier, + amount: ticket_1.amount + ticket_2.amount, + }; - Ok(()) + stack.push(StackItem::Option(OptionItem::Some(Box::new(ticket.into())))) } -} \ No newline at end of file +} diff --git a/michelson_vm/src/interpreter.rs b/michelson_vm/src/interpreter.rs index a15c76c..39db37f 100644 --- a/michelson_vm/src/interpreter.rs +++ b/michelson_vm/src/interpreter.rs @@ -174,6 +174,10 @@ impl Interpreter for Instruction { Instruction::Blake2B(instr) => instr.execute(stack), Instruction::HashKey(instr) => instr.execute(stack), Instruction::CheckSignature(instr) => instr.execute(stack), + Instruction::Ticket(instr) => instr.execute(stack, scope), + Instruction::ReadTicket(instr) => instr.execute(stack), + Instruction::SplitTicket(instr) => instr.execute(stack), + Instruction::JoinTickets(instr) => instr.execute(stack), _ => err_unsupported!(self.format()), }; trace_exit!(res.as_ref().err(), format!("Len {}", &stack.len()).as_str()); diff --git a/michelson_vm/src/types.rs b/michelson_vm/src/types.rs index c2ce69b..385afbc 100644 --- a/michelson_vm/src/types.rs +++ b/michelson_vm/src/types.rs @@ -17,8 +17,8 @@ pub mod option; pub mod or; pub mod pair; pub mod set; -pub mod timestamp; pub mod ticket; +pub mod timestamp; use derive_more::{Display, From, TryInto}; use ibig::{IBig, UBig}; @@ -161,10 +161,9 @@ pub enum BigMapItem { #[derive(Debug, Clone, PartialEq)] pub struct TicketItem { - pub source: Address, - pub identifier: Micheline, - pub identifier_type: Type, - pub amount: UBig, + pub source: AddressItem, + pub identifier: Box, + pub amount: NatItem, } #[derive(Debug, Display, Clone, From, TryInto, PartialEq, PartialOrd, Eq, Ord)] diff --git a/michelson_vm/src/types/nat.rs b/michelson_vm/src/types/nat.rs index 85efed8..1b1d256 100644 --- a/michelson_vm/src/types/nat.rs +++ b/michelson_vm/src/types/nat.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -use ibig::{IBig, UBig}; +use ibig::{ubig, IBig, UBig}; use std::fmt::Display; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; use tezos_michelson::michelson::{ @@ -40,6 +40,10 @@ impl NatItem { pub fn int(self) -> IntItem { IntItem(IBig::from(self.0)) } + + pub fn is_zero(&self) -> bool { + self.0 == ubig!(0) + } } impl Display for NatItem { diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index fee403d..04a0f39 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -4,33 +4,31 @@ use std::fmt::Display; -use ibig::UBig; -use tezos_core::types::encoded::Address; -use tezos_michelson::{michelson::types::{self, Type}, micheline::Micheline}; +use tezos_michelson::michelson::types::{self, Type}; -use crate::{ - types::TicketItem, - Result, -}; +use crate::{types::TicketItem, Result}; + +use super::{AddressItem, NatItem, StackItem}; impl TicketItem { - pub fn new(source: Address, identifier: Micheline, identifier_type: Type, amount: UBig) -> Self{ + pub fn new(source: AddressItem, identifier: StackItem, amount: NatItem) -> Self { Self { - source, - identifier, - identifier_type, - amount, + source: source, + identifier: Box::new(identifier), + amount: amount, } } pub fn get_type(&self) -> Result { - Ok(types::ticket(self.identifier_type.clone())) + Ok(types::ticket(self.identifier.get_type()?)) } - } impl Display for TicketItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("({:?} {:?} {})", self.source, self.identifier, self.amount)) + f.write_fmt(format_args!( + "({:?} {:?} {})", + self.source, self.identifier, self.amount + )) } -} \ No newline at end of file +}