From 09a0644a2c5d0d4e929e4db441bf5122cc163acc Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Tue, 25 Jul 2023 19:37:57 +0300 Subject: [PATCH 1/8] TicketItem for Stack and instruction skeleton --- .gitignore | 4 +- michelson_vm/src/formatter.rs | 5 ++ michelson_vm/src/instructions/mod.rs | 1 + michelson_vm/src/instructions/tickets.rs | 89 ++++++++++++++++++++++++ michelson_vm/src/typechecker.rs | 1 + michelson_vm/src/types.rs | 11 +++ michelson_vm/src/types/nat.rs | 4 ++ michelson_vm/src/types/ticket.rs | 36 ++++++++++ 8 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 michelson_vm/src/instructions/tickets.rs create mode 100644 michelson_vm/src/types/ticket.rs diff --git a/.gitignore b/.gitignore index e5adf79..4180d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ bin target tezos_kernel/target boot_kernel/target -.env \ No newline at end of file +.env +.bin +local.env diff --git a/michelson_vm/src/formatter.rs b/michelson_vm/src/formatter.rs index f25c4c1..0561dd0 100644 --- a/michelson_vm/src/formatter.rs +++ b/michelson_vm/src/formatter.rs @@ -113,6 +113,10 @@ impl Formatter for Instruction { Instruction::Blake2B(_) => "Blake2B".into(), Instruction::HashKey(_) => "HashKey".into(), Instruction::CheckSignature(_) => "CheckSignature".into(), + Instruction::Ticket(_) => "Ticket".into(), + Instruction::ReadTicket(_) => "ReadTicket".into(), + Instruction::SplitTicket(_) => "SplitTicket".into(), + Instruction::JoinTickets(_) => "JoinTicket".into(), _ => format!("{:?}", self), } } @@ -166,6 +170,7 @@ impl Formatter for Type { Type::Operation(_) => "operation".into(), Type::Parameter(ty) => format!("(parameter {})", ty.r#type.format()), Type::Storage(ty) => format!("(storage {})", ty.r#type.format()), + Type::Ticket(ty) => format!("(ticket {})", ty.r#type.format()), ty => format!("{:?}", ty), } } diff --git a/michelson_vm/src/instructions/mod.rs b/michelson_vm/src/instructions/mod.rs index 4b03101..6ddd4fb 100644 --- a/michelson_vm/src/instructions/mod.rs +++ b/michelson_vm/src/instructions/mod.rs @@ -12,3 +12,4 @@ mod lambda; mod math; mod scope; mod stack; +mod tickets; \ No newline at end of file diff --git a/michelson_vm/src/instructions/tickets.rs b/michelson_vm/src/instructions/tickets.rs new file mode 100644 index 0000000..105020e --- /dev/null +++ b/michelson_vm/src/instructions/tickets.rs @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2023 Baking Bad +// +// SPDX-License-Identifier: MIT + +use tezos_michelson::michelson::data::instructions::{ + Ticket, ReadTicket, SplitTicket, JoinTickets, +}; + +use crate::{ + err_mismatch, + interpreter::{ + ContextInterpreter, InterpreterContext, PureInterpreter, + }, + //pop_cast, + stack::Stack, + Result, types::{AddressItem, NatItem, StackItem, TicketItem, PairItem, OptionItem}, +}; + +impl ContextInterpreter for Ticket { + fn execute(&self, stack: &mut Stack, context: &mut impl InterpreterContext) -> 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(()) + } +} + +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 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])?; + + stack.push(StackItem::Pair(pair)); + stack.push(ticket_item); // return ticket back to stack + Ok(()) + } +} + +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 + + // TODO: if n + m != ticket.amount or n == 0 or m == 0 return none + stack.push(StackItem::Option(OptionItem::None())); + + // TODO: else return pair (ticket_n, ticket_m) + stack.push(StackItem::Option(OptionItem::Some())); + + // TODO: update balance in context? + + Ok(()) + } +} + +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?) + + // TODO: if ticket_a.source != ticket_b.source or ticket_a.identifier != ticket_b.identifier + stack.push(StackItem::Option(OptionItem::None())); + + // TODO: OR otherwise return Some(ticket) + stack.push(StackItem::Option(OptionItem::Some())); + + // TODO: update balance in context? + + Ok(()) + } +} \ No newline at end of file diff --git a/michelson_vm/src/typechecker.rs b/michelson_vm/src/typechecker.rs index d7c5311..a23a629 100644 --- a/michelson_vm/src/typechecker.rs +++ b/michelson_vm/src/typechecker.rs @@ -224,6 +224,7 @@ impl StackItem { StackItem::Operation(_) => Ok(types::operation()), StackItem::Lambda(item) => item.get_type(), StackItem::Contract(item) => Ok(item.get_type()), + StackItem::Ticket(item) => item.get_type(), } } diff --git a/michelson_vm/src/types.rs b/michelson_vm/src/types.rs index 03d58aa..c2ce69b 100644 --- a/michelson_vm/src/types.rs +++ b/michelson_vm/src/types.rs @@ -18,6 +18,7 @@ pub mod or; pub mod pair; pub mod set; pub mod timestamp; +pub mod ticket; use derive_more::{Display, From, TryInto}; use ibig::{IBig, UBig}; @@ -100,6 +101,7 @@ not_comparable!(LambdaItem); not_comparable!(ContractItem); not_comparable!(BigMapItem); not_comparable!(OperationItem); +not_comparable!(TicketItem); #[derive(Debug, Clone, PartialEq)] pub enum InternalContent { @@ -157,6 +159,14 @@ pub enum BigMapItem { Ptr(i64), } +#[derive(Debug, Clone, PartialEq)] +pub struct TicketItem { + pub source: Address, + pub identifier: Micheline, + pub identifier_type: Type, + pub amount: UBig, +} + #[derive(Debug, Display, Clone, From, TryInto, PartialEq, PartialOrd, Eq, Ord)] pub enum StackItem { Unit(UnitItem), @@ -182,6 +192,7 @@ pub enum StackItem { Map(MapItem), BigMap(BigMapItem), Lambda(LambdaItem), + Ticket(TicketItem), } impl AsMut for StackItem { diff --git a/michelson_vm/src/types/nat.rs b/michelson_vm/src/types/nat.rs index e919954..85efed8 100644 --- a/michelson_vm/src/types/nat.rs +++ b/michelson_vm/src/types/nat.rs @@ -18,6 +18,10 @@ use crate::{ }; impl NatItem { + pub fn new(nat: UBig) -> Self { + Self(nat) + } + pub fn from_data(data: Data) -> Result { let val: UBig = match data { Data::Int(val) => val.try_into()?, diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs new file mode 100644 index 0000000..fee403d --- /dev/null +++ b/michelson_vm/src/types/ticket.rs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Baking Bad +// +// SPDX-License-Identifier: MIT + +use std::fmt::Display; + +use ibig::UBig; +use tezos_core::types::encoded::Address; +use tezos_michelson::{michelson::types::{self, Type}, micheline::Micheline}; + +use crate::{ + types::TicketItem, + Result, +}; + +impl TicketItem { + pub fn new(source: Address, identifier: Micheline, identifier_type: Type, amount: UBig) -> Self{ + Self { + source, + identifier, + identifier_type, + amount, + } + } + + pub fn get_type(&self) -> Result { + Ok(types::ticket(self.identifier_type.clone())) + } + +} + +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)) + } +} \ No newline at end of file From 4b3d94a561bfe3aa389b2e323026840063c24d94 Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Fri, 28 Jul 2023 01:18:47 +0300 Subject: [PATCH 2/8] 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 +} From a3451e0980fdbc2883bb240b8d9eefcaf3d502eb Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Mon, 14 Aug 2023 16:48:44 +0300 Subject: [PATCH 3/8] Update ticket balance interface --- michelson_vm/src/instructions/contract.rs | 15 ++ michelson_vm/src/instructions/stack.rs | 22 +- michelson_vm/src/instructions/tickets.rs | 30 ++- michelson_vm/src/interpreter.rs | 36 ++- michelson_vm/src/mock.rs | 13 +- michelson_vm/src/store.rs | 13 +- michelson_vm/src/types/nat.rs | 4 + michelson_vm/src/types/ticket.rs | 255 +++++++++++++++++++++- 8 files changed, 363 insertions(+), 25 deletions(-) diff --git a/michelson_vm/src/instructions/contract.rs b/michelson_vm/src/instructions/contract.rs index 8fe2fc2..554924d 100644 --- a/michelson_vm/src/instructions/contract.rs +++ b/michelson_vm/src/instructions/contract.rs @@ -2,12 +2,14 @@ // // SPDX-License-Identifier: MIT +use ibig::IBig; use tezos_core::types::{encoded, encoded::Encoded}; use tezos_michelson::michelson::data::instructions::{ Address, Contract, ImplicitAccount, Self_, TransferTokens, }; use tezos_michelson::michelson::{annotations::Annotation, types, types::Type}; +use crate::interpreter::TicketStorage; use crate::{ entrypoints::search_entrypoint, err_mismatch, @@ -147,6 +149,19 @@ impl Interpreter for TransferTokens { // TODO: support big_map ownership transfer } + param.iter_tickets(&mut |t| { + let amount: IBig = t.amount.value().into(); + context.update_ticket_balance( + scope.self_address.clone().into(), + t.identifier + .clone() + .into_micheline(&t.identifier.get_type().unwrap()) + .unwrap(), + scope.self_address.clone().into(), + -amount, + ) + })?; + let content = InternalContent::Transaction { destination, parameter: param.into_micheline(¶m_type)?, diff --git a/michelson_vm/src/instructions/stack.rs b/michelson_vm/src/instructions/stack.rs index 2e09bd5..c5fb958 100644 --- a/michelson_vm/src/instructions/stack.rs +++ b/michelson_vm/src/instructions/stack.rs @@ -7,7 +7,11 @@ use std::borrow::Borrow; use tezos_michelson::michelson::data::instructions::{Dig, Drop, Dug, Dup, Push, Swap}; use crate::{ - err_unsupported, interpreter::PureInterpreter, stack::Stack, types::StackItem, Result, + err_unsupported, + interpreter::{Interpreter, PureInterpreter, TicketStorage}, + stack::Stack, + types::StackItem, + InterpreterContext, OperationScope, Result, }; impl PureInterpreter for Push { @@ -18,14 +22,20 @@ impl PureInterpreter for Push { } } -impl PureInterpreter for Drop { - fn execute(&self, stack: &mut Stack) -> Result<()> { +impl Interpreter for Drop { + fn execute( + &self, + stack: &mut Stack, + scope: &OperationScope, + context: &mut impl InterpreterContext, + ) -> Result<()> { let count: usize = match &self.n { Some(n) => n.try_into()?, None => 1, }; for _ in 0..count { - stack.pop()?; + let item = stack.pop()?; + item.drop_tickets(&scope.self_address, context)?; } Ok(()) } @@ -43,8 +53,8 @@ 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"); + if res.has_tickets() { + return err_unsupported!("proto.alpha.michelson_v1.non_dupable_type"); } stack.push(res) diff --git a/michelson_vm/src/instructions/tickets.rs b/michelson_vm/src/instructions/tickets.rs index c1a0def..682cff8 100644 --- a/michelson_vm/src/instructions/tickets.rs +++ b/michelson_vm/src/instructions/tickets.rs @@ -9,7 +9,7 @@ use tezos_michelson::michelson::{ use crate::{ err_mismatch, - interpreter::{PureInterpreter, ScopedInterpreter}, + interpreter::{Interpreter, InterpreterContext, PureInterpreter}, pop_cast, stack::Stack, typechecker::check_type_comparable, @@ -17,8 +17,13 @@ use crate::{ OperationScope, Result, }; -impl ScopedInterpreter for Ticket { - fn execute(&self, stack: &mut Stack, scope: &OperationScope) -> Result<()> { +impl Interpreter for Ticket { + fn execute( + &self, + stack: &mut Stack, + scope: &OperationScope, + context: &mut impl InterpreterContext, + ) -> Result<()> { let identifier = stack.pop()?; let identifier_ty = identifier.get_type()?; check_type_comparable(&identifier_ty)?; @@ -33,9 +38,16 @@ impl ScopedInterpreter for Ticket { let ticket = TicketItem { source: AddressItem::new(scope.self_address.clone().into()), identifier: Box::new(identifier), - amount: amount, + amount: amount.clone(), }; + context.update_ticket_balance( + scope.self_address.clone().into(), + ticket.identifier.clone().into_micheline(&identifier_ty)?, + scope.self_address.clone().into(), + amount.value().into(), + )?; + stack.push(StackItem::Option(OptionItem::Some(Box::new(ticket.into())))) } } @@ -63,10 +75,7 @@ impl PureInterpreter for SplitTicket { 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)) - ) + return err_mismatch!("Pair Nat Nat", StackItem::Pair(PairItem::new(s1, s2))) } }; @@ -97,10 +106,7 @@ impl PureInterpreter for JoinTickets { 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)) - ) + return err_mismatch!("Pair Ticket Ticket", StackItem::Pair(PairItem::new(s1, s2))) } }; diff --git a/michelson_vm/src/interpreter.rs b/michelson_vm/src/interpreter.rs index 39db37f..4ef2a69 100644 --- a/michelson_vm/src/interpreter.rs +++ b/michelson_vm/src/interpreter.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT +use ibig::IBig; use tezos_core::types::{ encoded::{Address, ChainId, ContractAddress, ImplicitAddress, ScriptExprHash}, mutez::Mutez, @@ -14,7 +15,7 @@ use crate::{ formatter::Formatter, stack::Stack, trace_enter, trace_exit, - types::{BigMapDiff, StackItem}, + types::{BigMapDiff, StackItem, TicketItem}, Result, }; @@ -36,6 +37,13 @@ pub trait InterpreterContext { key_hash: ScriptExprHash, value: Option, ) -> Result<()>; + fn update_ticket_balance( + &mut self, + tickiter: Address, + identifier: Micheline, + owner: Address, + value: IBig, + ) -> Result<()>; } pub struct OperationScope { @@ -82,6 +90,28 @@ pub trait LazyStorage { fn try_aggregate(&mut self, output: &mut Vec, ty: &Type) -> Result<()>; } +pub trait TicketStorage { + fn has_tickets(&self) -> bool; + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()>; + fn drop_tickets( + &self, + owner: &ContractAddress, + context: &mut impl InterpreterContext, + ) -> Result<()> { + self.iter_tickets(&mut |t| { + let amount: IBig = t.amount.value().into(); + context.update_ticket_balance( + t.source.clone().unwrap(), + t.identifier + .clone() + .into_micheline(&t.identifier.get_type()?)?, + owner.clone().into(), + -amount, + ) + }) + } +} + impl Interpreter for Instruction { fn execute( &self, @@ -93,7 +123,7 @@ impl Interpreter for Instruction { let res = match self { Instruction::Sequence(seq) => return seq.execute(stack, scope, context), Instruction::Push(instr) => instr.execute(stack), - Instruction::Drop(instr) => instr.execute(stack), + Instruction::Drop(instr) => instr.execute(stack, scope, context), Instruction::Dup(instr) => instr.execute(stack), Instruction::Swap(instr) => instr.execute(stack), Instruction::Dig(instr) => instr.execute(stack), @@ -174,7 +204,7 @@ 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::Ticket(instr) => instr.execute(stack, scope, context), Instruction::ReadTicket(instr) => instr.execute(stack), Instruction::SplitTicket(instr) => instr.execute(stack), Instruction::JoinTickets(instr) => instr.execute(stack), diff --git a/michelson_vm/src/mock.rs b/michelson_vm/src/mock.rs index 976079a..b8f939c 100644 --- a/michelson_vm/src/mock.rs +++ b/michelson_vm/src/mock.rs @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: MIT +use ibig::IBig; use std::collections::HashMap; use tezos_core::types::{ - encoded::{self, Encoded}, + encoded::{self, Address, Encoded}, mutez::Mutez, }; use tezos_michelson::micheline::{primitive_application, Micheline}; @@ -141,4 +142,14 @@ impl InterpreterContext for MockContext { }; Ok(()) } + + fn update_ticket_balance( + &mut self, + tickiter: Address, + identifier: Micheline, + owner: Address, + value: IBig, + ) -> Result<()> { + todo!() + } } diff --git a/michelson_vm/src/store.rs b/michelson_vm/src/store.rs index 92cb197..0c75bfd 100644 --- a/michelson_vm/src/store.rs +++ b/michelson_vm/src/store.rs @@ -2,8 +2,9 @@ // // SPDX-License-Identifier: MIT +use ibig::IBig; use layered_store::{LayeredStore, StoreBackend}; -use tezos_core::types::encoded::{ContractAddress, Encoded, ScriptExprHash}; +use tezos_core::types::encoded::{Address, ContractAddress, Encoded, ScriptExprHash}; use tezos_michelson::micheline::Micheline; use crate::{error::err_into, InterpreterContext, Result}; @@ -77,4 +78,14 @@ impl InterpreterContext for LayeredStore { ) .map_err(err_into) } + + fn update_ticket_balance( + &mut self, + tickiter: Address, + identifier: Micheline, + owner: Address, + value: IBig, + ) -> Result<()> { + todo!() + } } diff --git a/michelson_vm/src/types/nat.rs b/michelson_vm/src/types/nat.rs index 1b1d256..31efe7a 100644 --- a/michelson_vm/src/types/nat.rs +++ b/michelson_vm/src/types/nat.rs @@ -44,6 +44,10 @@ impl NatItem { pub fn is_zero(&self) -> bool { self.0 == ubig!(0) } + + pub fn value(&self) -> &UBig { + &self.0 + } } impl Display for NatItem { diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 04a0f39..08328a7 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -6,9 +6,12 @@ use std::fmt::Display; use tezos_michelson::michelson::types::{self, Type}; -use crate::{types::TicketItem, Result}; +use crate::{interpreter::TicketStorage, types::TicketItem, Result}; -use super::{AddressItem, NatItem, StackItem}; +use super::{ + AddressItem, BigMapItem, ListItem, MapItem, NatItem, OperationItem, OptionItem, OrItem, + PairItem, StackItem, +}; impl TicketItem { pub fn new(source: AddressItem, identifier: StackItem, amount: NatItem) -> Self { @@ -32,3 +35,251 @@ impl Display for TicketItem { )) } } + +impl TicketStorage for StackItem { + fn has_tickets(&self) -> bool { + match self { + StackItem::BigMap(item) => item.has_tickets(), + StackItem::Option(item) => item.has_tickets(), + StackItem::Or(item) => item.has_tickets(), + StackItem::Pair(item) => item.has_tickets(), + StackItem::List(item) => item.has_tickets(), + StackItem::Map(item) => item.has_tickets(), + StackItem::Ticket(item) => item.has_tickets(), + StackItem::Operation(item) => item.has_tickets(), + _ => false, + } + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + match self { + StackItem::BigMap(item) => item.iter_tickets(action), + StackItem::Option(item) => item.iter_tickets(action), + StackItem::Or(item) => item.iter_tickets(action), + StackItem::Pair(item) => item.iter_tickets(action), + StackItem::List(item) => item.iter_tickets(action), + StackItem::Map(item) => item.iter_tickets(action), + StackItem::Ticket(item) => item.iter_tickets(action), + StackItem::Operation(item) => item.iter_tickets(action), + _ => Ok(()), + } + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // match self { + // StackItem::BigMap(item) => item.drop_tickets(owner, context), + // StackItem::Option(item) => item.drop_tickets(owner, context), + // StackItem::Or(item) => item.drop_tickets(owner, context), + // StackItem::Pair(item) => item.drop_tickets(owner, context), + // StackItem::List(item) => item.drop_tickets(owner, context), + // StackItem::Map(item) => item.drop_tickets(owner, context), + // StackItem::Ticket(item) => item.drop_tickets(owner, context), + // StackItem::Operation(item) => item.drop_tickets(owner, context), + // _ => Ok(()), + // } + // } +} + +impl TicketStorage for TicketItem { + fn has_tickets(&self) -> bool { + true + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + action(self) + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // let amount: IBig = self.amount.value().into(); + // context.update_ticket_balance( + // self.source.clone().0, + // self.identifier + // .clone() + // .into_micheline(&self.identifier.get_type()?)?, + // owner.clone().into(), + // -amount, + // ) + // } +} + +impl TicketStorage for BigMapItem { + fn has_tickets(&self) -> bool { + todo!() + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + todo!() + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // todo!() + // } +} + +impl TicketStorage for OptionItem { + fn has_tickets(&self) -> bool { + match self { + Self::None(_) => false, + Self::Some(val) => val.has_tickets(), + } + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + match self { + Self::None(_) => Ok(()), + Self::Some(val) => val.iter_tickets(action), + } + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // match self { + // Self::None(_) => Ok(()), + // Self::Some(val) => val.drop_tickets(owner, context), + // } + // } +} + +impl TicketStorage for OrItem { + fn has_tickets(&self) -> bool { + match self { + Self::Left(var) => var.value.has_tickets(), + Self::Right(var) => var.value.has_tickets(), + } + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + match self { + Self::Left(var) => var.value.iter_tickets(action), + Self::Right(var) => var.value.iter_tickets(action), + } + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // match self { + // Self::Left(var) => var.value.drop_tickets(owner, context), + // Self::Right(var) => var.value.drop_tickets(owner, context), + // } + // } +} + +impl TicketStorage for PairItem { + fn has_tickets(&self) -> bool { + self.0 .0.has_tickets() || self.0 .1.has_tickets() + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + self.0 .0.iter_tickets(action)?; + self.0 .1.iter_tickets(action) + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // self.0 .0.drop_tickets(owner, context)?; + // self.0 .1.drop_tickets(owner, context) + // } +} + +impl TicketStorage for ListItem { + fn has_tickets(&self) -> bool { + for e in &self.outer_value { + if e.has_tickets() { + return true; + } + } + false + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + self.outer_value + .iter() + .map(|e| e.iter_tickets(action)) + .collect() + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // self.outer_value + // .iter() + // .map(|e| e.drop_tickets(owner, context)) + // .collect() + // } +} + +impl TicketStorage for MapItem { + fn has_tickets(&self) -> bool { + for (k, v) in &self.outer_value { + if k.has_tickets() || v.has_tickets() { + return true; + } + } + false + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + self.outer_value + .iter() + .map(|(k, v)| -> Result<()> { + k.iter_tickets(action)?; + v.iter_tickets(action) + }) + .collect() + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // self.outer_value + // .iter() + // .map(|(k, v)| -> Result<()> { + // k.drop_tickets(owner, context)?; + // v.drop_tickets(owner, context) + // }) + // .collect() + // } +} + +impl TicketStorage for OperationItem { + fn has_tickets(&self) -> bool { + todo!() + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + todo!() + } + + // fn drop_tickets( + // &self, + // owner: &ContractAddress, + // context: &mut impl InterpreterContext, + // ) -> Result<()> { + // todo!() + // } +} From 9ac337578bd53972b0e0155e94ce345f2094895e Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Sat, 19 Aug 2023 14:35:35 +0300 Subject: [PATCH 4/8] OperationItem --- michelson_vm/src/instructions/contract.rs | 17 +-- michelson_vm/src/script.rs | 2 +- michelson_vm/src/typechecker.rs | 2 + michelson_vm/src/types.rs | 7 +- michelson_vm/src/types/operation.rs | 44 +++++++- michelson_vm/src/types/ticket.rs | 122 +++------------------- 6 files changed, 69 insertions(+), 125 deletions(-) diff --git a/michelson_vm/src/instructions/contract.rs b/michelson_vm/src/instructions/contract.rs index 554924d..11c3bb8 100644 --- a/michelson_vm/src/instructions/contract.rs +++ b/michelson_vm/src/instructions/contract.rs @@ -21,7 +21,7 @@ use crate::{ stack::Stack, trace_log, typechecker::check_types_equal, - types::{AddressItem, ContractItem, InternalContent, OperationItem, OptionItem, StackItem}, + types::{AddressItem, ContractItem, OperationItem, OptionItem, StackItem}, Error, Result, }; @@ -162,14 +162,15 @@ impl Interpreter for TransferTokens { ) })?; - let content = InternalContent::Transaction { - destination, - parameter: param.into_micheline(¶m_type)?, - amount: amount.try_into()?, - source: scope.source.clone(), - }; + // let content = InternalContent::Transaction { + // destination, + // parameter: param.into_micheline(¶m_type)?, + // amount: amount.try_into()?, + // source: scope.source.clone(), + // }; - let res = OperationItem::new(content); + //let res = OperationItem::new(content); + let res = OperationItem::new(destination, param, param_type, amount, scope.source.clone()); stack.push(res.into()) } } diff --git a/michelson_vm/src/script.rs b/michelson_vm/src/script.rs index 33bcd24..c940ef7 100644 --- a/michelson_vm/src/script.rs +++ b/michelson_vm/src/script.rs @@ -97,7 +97,7 @@ impl MichelsonScript { match item { StackItem::Operation(mut op) => { op.aggregate_diff(&mut big_map_diff); - operations.push(op.into_content()) + operations.push(op.into_content()?) } item => return err_mismatch!("OperationItem", item), } diff --git a/michelson_vm/src/typechecker.rs b/michelson_vm/src/typechecker.rs index a23a629..6cc9048 100644 --- a/michelson_vm/src/typechecker.rs +++ b/michelson_vm/src/typechecker.rs @@ -130,6 +130,8 @@ pub fn check_types_equal(lhs: &Type, rhs: &Type) -> Result<()> { } } +//pub fn contains_types + impl StackItem { pub fn from_data(data: Data, ty: &Type) -> Result { match ty { diff --git a/michelson_vm/src/types.rs b/michelson_vm/src/types.rs index 385afbc..b789d90 100644 --- a/michelson_vm/src/types.rs +++ b/michelson_vm/src/types.rs @@ -116,7 +116,12 @@ pub enum InternalContent { #[derive(Debug, Clone)] pub struct OperationItem { // domain - content: InternalContent, + //content: InternalContent, + destination: Address, + param: Box, + param_type: Type, + amount: MutezItem, + source: ImplicitAddress, big_map_diff: Vec, } diff --git a/michelson_vm/src/types/operation.rs b/michelson_vm/src/types/operation.rs index 022de68..13160d2 100644 --- a/michelson_vm/src/types/operation.rs +++ b/michelson_vm/src/types/operation.rs @@ -4,18 +4,48 @@ use std::fmt::Display; +use tezos_core::types::encoded::{Address, ImplicitAddress}; +use tezos_michelson::michelson::types::Type; + use crate::types::{BigMapDiff, InternalContent, OperationItem}; +use crate::Result; + +use super::{MutezItem, StackItem}; impl OperationItem { - pub fn new(content: InternalContent) -> Self { + // pub fn new(content: InternalContent) -> Self { + // Self { + // content, + // big_map_diff: Vec::new(), + // } + // } + + pub fn new( + destination: Address, + param: StackItem, + param_type: Type, + amount: MutezItem, + source: ImplicitAddress, + ) -> Self { Self { - content, + destination: destination, + param: Box::new(param), + param_type: param_type, + amount: amount, + source: source, big_map_diff: Vec::new(), } } - pub fn into_content(self) -> InternalContent { - self.content + pub fn into_content(self) -> Result { + let content = InternalContent::Transaction { + destination: self.destination, + parameter: self.param.into_micheline(&self.param_type)?, + amount: self.amount.try_into()?, + source: self.source.clone(), + }; + + Ok(content) } pub fn aggregate_diff(&mut self, big_map_diff: &mut Vec) { @@ -26,7 +56,11 @@ impl OperationItem { impl PartialEq for OperationItem { fn eq(&self, other: &Self) -> bool { // For testing purposes - self.content == other.content + self.destination == other.destination + && self.param == other.param + && self.param_type == other.param_type + && self.amount == other.amount + && self.source == other.source } } diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 08328a7..67da5f8 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -64,24 +64,6 @@ impl TicketStorage for StackItem { _ => Ok(()), } } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // match self { - // StackItem::BigMap(item) => item.drop_tickets(owner, context), - // StackItem::Option(item) => item.drop_tickets(owner, context), - // StackItem::Or(item) => item.drop_tickets(owner, context), - // StackItem::Pair(item) => item.drop_tickets(owner, context), - // StackItem::List(item) => item.drop_tickets(owner, context), - // StackItem::Map(item) => item.drop_tickets(owner, context), - // StackItem::Ticket(item) => item.drop_tickets(owner, context), - // StackItem::Operation(item) => item.drop_tickets(owner, context), - // _ => Ok(()), - // } - // } } impl TicketStorage for TicketItem { @@ -92,40 +74,24 @@ impl TicketStorage for TicketItem { fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { action(self) } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // let amount: IBig = self.amount.value().into(); - // context.update_ticket_balance( - // self.source.clone().0, - // self.identifier - // .clone() - // .into_micheline(&self.identifier.get_type()?)?, - // owner.clone().into(), - // -amount, - // ) - // } } impl TicketStorage for BigMapItem { fn has_tickets(&self) -> bool { - todo!() + match self { + BigMapItem::Diff(val) => todo!(), + BigMapItem::Map(val) => val.has_tickets(), + BigMapItem::Ptr(_) => false, + } } fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { - todo!() + match self { + BigMapItem::Diff(val) => todo!(), + BigMapItem::Map(val) => val.iter_tickets(action), + BigMapItem::Ptr(_) => Ok(()), + } } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // todo!() - // } } impl TicketStorage for OptionItem { @@ -142,17 +108,6 @@ impl TicketStorage for OptionItem { Self::Some(val) => val.iter_tickets(action), } } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // match self { - // Self::None(_) => Ok(()), - // Self::Some(val) => val.drop_tickets(owner, context), - // } - // } } impl TicketStorage for OrItem { @@ -169,17 +124,6 @@ impl TicketStorage for OrItem { Self::Right(var) => var.value.iter_tickets(action), } } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // match self { - // Self::Left(var) => var.value.drop_tickets(owner, context), - // Self::Right(var) => var.value.drop_tickets(owner, context), - // } - // } } impl TicketStorage for PairItem { @@ -191,15 +135,6 @@ impl TicketStorage for PairItem { self.0 .0.iter_tickets(action)?; self.0 .1.iter_tickets(action) } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // self.0 .0.drop_tickets(owner, context)?; - // self.0 .1.drop_tickets(owner, context) - // } } impl TicketStorage for ListItem { @@ -218,17 +153,6 @@ impl TicketStorage for ListItem { .map(|e| e.iter_tickets(action)) .collect() } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // self.outer_value - // .iter() - // .map(|e| e.drop_tickets(owner, context)) - // .collect() - // } } impl TicketStorage for MapItem { @@ -250,36 +174,14 @@ impl TicketStorage for MapItem { }) .collect() } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // self.outer_value - // .iter() - // .map(|(k, v)| -> Result<()> { - // k.drop_tickets(owner, context)?; - // v.drop_tickets(owner, context) - // }) - // .collect() - // } } impl TicketStorage for OperationItem { fn has_tickets(&self) -> bool { - todo!() + self.param.has_tickets() } fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { - todo!() + self.param.iter_tickets(action) } - - // fn drop_tickets( - // &self, - // owner: &ContractAddress, - // context: &mut impl InterpreterContext, - // ) -> Result<()> { - // todo!() - // } } From 9dceae95572609be1d64730c690d509840595347 Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Sun, 20 Aug 2023 13:02:51 +0300 Subject: [PATCH 5/8] Save tickets updates to context --- layered_store/src/store.rs | 14 ++++ michelson_vm/src/error.rs | 8 +++ michelson_vm/src/instructions/contract.rs | 17 ++--- michelson_vm/src/instructions/stack.rs | 4 +- michelson_vm/src/instructions/tickets.rs | 7 +- michelson_vm/src/interpreter.rs | 26 +++++--- michelson_vm/src/mock.rs | 30 +++++++-- michelson_vm/src/script.rs | 19 ++++++ michelson_vm/src/store.rs | 76 +++++++++++++++++++-- michelson_vm/src/typechecker.rs | 2 - michelson_vm/src/types.rs | 1 - michelson_vm/src/types/operation.rs | 15 ++--- michelson_vm/src/types/ticket.rs | 81 +++++++++++++++++++++-- 13 files changed, 242 insertions(+), 58 deletions(-) diff --git a/layered_store/src/store.rs b/layered_store/src/store.rs index 25764b7..7c0681a 100644 --- a/layered_store/src/store.rs +++ b/layered_store/src/store.rs @@ -42,6 +42,7 @@ pub struct LayeredStore { backend: Backend, pending_state: HashMap>, modified_keys: HashSet, + //tmp_state: HashMap>, } impl LayeredStore { @@ -50,6 +51,7 @@ impl LayeredStore { backend, pending_state: HashMap::new(), modified_keys: HashSet::new(), + //tmp_state: Hash } } @@ -106,6 +108,18 @@ impl LayeredStore { Ok(()) } + pub fn set_tmp(&mut self, key: String, val: Option) -> Result<()> { + match val { + Some(value) => self + .pending_state + .insert(key.clone(), Some((Box::new(value), Box::new(T::serialize)))), + None => self.pending_state.insert(key.clone(), None), + }; + Ok(()) + } + + //pub fn iter_pending(&mut self, key_prefix: String, ) + pub fn commit(&mut self) -> Result<()> { let modified_keys: Vec = self.modified_keys.drain().collect(); for key in modified_keys { diff --git a/michelson_vm/src/error.rs b/michelson_vm/src/error.rs index f86b245..851bf2f 100644 --- a/michelson_vm/src/error.rs +++ b/michelson_vm/src/error.rs @@ -88,6 +88,14 @@ pub enum Error { MutezUnderflow, #[display(fmt = "GeneralOverflow")] GeneralOverflow, + #[display(fmt = "NegativeTicketBalance")] + NegativeTicketBalance, + #[display(fmt = "NonDupableType")] + NonDupableType, + #[display(fmt = "UnexpectedTicketOwner")] + UnexpectedTicketOwner, + #[display(fmt = "ForbiddenZeroAmountTicket")] + ForbiddenZeroAmountTicket, } pub type Result = std::result::Result; diff --git a/michelson_vm/src/instructions/contract.rs b/michelson_vm/src/instructions/contract.rs index 11c3bb8..c74fcc6 100644 --- a/michelson_vm/src/instructions/contract.rs +++ b/michelson_vm/src/instructions/contract.rs @@ -152,24 +152,17 @@ impl Interpreter for TransferTokens { param.iter_tickets(&mut |t| { let amount: IBig = t.amount.value().into(); context.update_ticket_balance( - scope.self_address.clone().into(), - t.identifier + &scope.self_address.clone().into(), + &t.identifier .clone() - .into_micheline(&t.identifier.get_type().unwrap()) + .into_micheline(&t.identifier.get_type()?) .unwrap(), - scope.self_address.clone().into(), + &t.identifier.get_type()?, + &scope.self_address.clone().into(), -amount, ) })?; - // let content = InternalContent::Transaction { - // destination, - // parameter: param.into_micheline(¶m_type)?, - // amount: amount.try_into()?, - // source: scope.source.clone(), - // }; - - //let res = OperationItem::new(content); let res = OperationItem::new(destination, param, param_type, amount, scope.source.clone()); stack.push(res.into()) } diff --git a/michelson_vm/src/instructions/stack.rs b/michelson_vm/src/instructions/stack.rs index c5fb958..0b4e001 100644 --- a/michelson_vm/src/instructions/stack.rs +++ b/michelson_vm/src/instructions/stack.rs @@ -11,7 +11,7 @@ use crate::{ interpreter::{Interpreter, PureInterpreter, TicketStorage}, stack::Stack, types::StackItem, - InterpreterContext, OperationScope, Result, + Error, InterpreterContext, OperationScope, Result, }; impl PureInterpreter for Push { @@ -54,7 +54,7 @@ impl PureInterpreter for Dup { let res = stack.dup_at(n - 1)?; if res.has_tickets() { - return err_unsupported!("proto.alpha.michelson_v1.non_dupable_type"); + return Err(Error::NonDupableType); // proto.alpha.michelson_v1.non_dupable_type } stack.push(res) diff --git a/michelson_vm/src/instructions/tickets.rs b/michelson_vm/src/instructions/tickets.rs index 682cff8..f97d496 100644 --- a/michelson_vm/src/instructions/tickets.rs +++ b/michelson_vm/src/instructions/tickets.rs @@ -42,9 +42,10 @@ impl Interpreter for Ticket { }; context.update_ticket_balance( - scope.self_address.clone().into(), - ticket.identifier.clone().into_micheline(&identifier_ty)?, - scope.self_address.clone().into(), + &scope.self_address.clone().into(), + &ticket.identifier.clone().into_micheline(&identifier_ty)?, + &identifier_ty, + &scope.self_address.clone().into(), amount.value().into(), )?; diff --git a/michelson_vm/src/interpreter.rs b/michelson_vm/src/interpreter.rs index 4ef2a69..ad091a7 100644 --- a/michelson_vm/src/interpreter.rs +++ b/michelson_vm/src/interpreter.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -use ibig::IBig; +use ibig::{IBig, UBig}; use tezos_core::types::{ encoded::{Address, ChainId, ContractAddress, ImplicitAddress, ScriptExprHash}, mutez::Mutez, @@ -15,7 +15,7 @@ use crate::{ formatter::Formatter, stack::Stack, trace_enter, trace_exit, - types::{BigMapDiff, StackItem, TicketItem}, + types::{ticket::TicketBalanceDiff, BigMapDiff, StackItem, TicketItem}, Result, }; @@ -37,13 +37,22 @@ pub trait InterpreterContext { key_hash: ScriptExprHash, value: Option, ) -> Result<()>; + fn get_ticket_balance( + &mut self, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, + ) -> Result; fn update_ticket_balance( &mut self, - tickiter: Address, - identifier: Micheline, - owner: Address, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, value: IBig, ) -> Result<()>; + fn aggregate_ticket_updates(&self) -> Vec; } pub struct OperationScope { @@ -101,11 +110,12 @@ pub trait TicketStorage { self.iter_tickets(&mut |t| { let amount: IBig = t.amount.value().into(); context.update_ticket_balance( - t.source.clone().unwrap(), - t.identifier + &t.source.clone().unwrap(), + &t.identifier .clone() .into_micheline(&t.identifier.get_type()?)?, - owner.clone().into(), + &t.identifier.get_type()?, + &owner.clone().into(), -amount, ) }) diff --git a/michelson_vm/src/mock.rs b/michelson_vm/src/mock.rs index b8f939c..479f268 100644 --- a/michelson_vm/src/mock.rs +++ b/michelson_vm/src/mock.rs @@ -8,12 +8,17 @@ use tezos_core::types::{ encoded::{self, Address, Encoded}, mutez::Mutez, }; -use tezos_michelson::micheline::{primitive_application, Micheline}; use tezos_michelson::michelson::types::unit; +use tezos_michelson::{ + micheline::{primitive_application, Micheline}, + michelson::types::Type, +}; use crate::{ interpreter::{InterpreterContext, OperationScope}, - trace_log, Result, + trace_log, + types::ticket::TicketBalanceDiff, + Result, }; pub const CHAIN_ID: &str = "NetXP2FfcNxFANL"; @@ -143,13 +148,28 @@ impl InterpreterContext for MockContext { Ok(()) } + fn get_ticket_balance( + &mut self, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, + ) -> Result { + todo!() + } + fn update_ticket_balance( &mut self, - tickiter: Address, - identifier: Micheline, - owner: Address, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, value: IBig, ) -> Result<()> { todo!() } + + fn aggregate_ticket_updates(&self) -> Vec { + todo!() + } } diff --git a/michelson_vm/src/script.rs b/michelson_vm/src/script.rs index c940ef7..03b7b9e 100644 --- a/michelson_vm/src/script.rs +++ b/michelson_vm/src/script.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT +use tezos_core::types::encoded::Address; use tezos_michelson::micheline::{primitive_application, sequence, sequence::Sequence, Micheline}; use tezos_michelson::michelson::{ data::Instruction, @@ -9,6 +10,7 @@ use tezos_michelson::michelson::{ types::{Code, Parameter, Storage, Type}, }; +use crate::interpreter::TicketStorage; use crate::{ entrypoints::normalize_parameter, err_mismatch, err_unsupported, internal_error, @@ -60,6 +62,23 @@ impl MichelsonScript { let param = normalize_parameter(parameter, entrypoint, &self.parameter_type)?; let param_item = StackItem::from_micheline(param, &self.parameter_type)?; + // check tickets + let mut has_tickets = false; + + param_item.iter_tickets(&mut |t| -> Result<()> { + has_tickets = true; + if t.amount.is_zero() { + return Err(Error::ForbiddenZeroAmountTicket); + } + Ok(()) + })?; + + if has_tickets { + if let Address::Implicit(_) = scope.sender { + return Err(Error::UnexpectedTicketOwner); + } + } + let storage = scope.storage.clone().normalized(); let storage_item = StackItem::from_micheline(storage, &self.storage_type)?; diff --git a/michelson_vm/src/store.rs b/michelson_vm/src/store.rs index 0c75bfd..e78c8e3 100644 --- a/michelson_vm/src/store.rs +++ b/michelson_vm/src/store.rs @@ -2,12 +2,24 @@ // // SPDX-License-Identifier: MIT -use ibig::IBig; +use ibig::{IBig, UBig}; use layered_store::{LayeredStore, StoreBackend}; -use tezos_core::types::encoded::{Address, ContractAddress, Encoded, ScriptExprHash}; -use tezos_michelson::micheline::Micheline; +use tezos_core::types::{ + encoded::{Address, ContractAddress, Encoded, ScriptExprHash}, + number::Nat, +}; +use tezos_michelson::{micheline::Micheline, michelson::types::Type}; -use crate::{error::err_into, InterpreterContext, Result}; +use crate::{error::err_into, types::ticket::TicketBalanceDiff, Error, InterpreterContext, Result}; + +fn get_ticket_key_hash( + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, +) -> ScriptExprHash { + todo!() +} impl InterpreterContext for LayeredStore { fn set_contract_type(&mut self, address: ContractAddress, value: Micheline) -> Result<()> { @@ -79,13 +91,63 @@ impl InterpreterContext for LayeredStore { .map_err(err_into) } + fn get_ticket_balance( + &mut self, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, + ) -> Result { + let key_hash = get_ticket_key_hash(tickiter, identifier, identifier_ty, owner); + + let balance = match self + .get::(format!("/context/tickets/{}", key_hash.value())) + .map_err(err_into)? + { + Some(nat) => UBig::from(nat), + None => UBig::from(0u32), + }; + Ok(balance) + } + fn update_ticket_balance( &mut self, - tickiter: Address, - identifier: Micheline, - owner: Address, + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, value: IBig, ) -> Result<()> { + let current_balance: UBig = + self.get_ticket_balance(tickiter, identifier, identifier_ty, owner)?; + let updated_balance: IBig = IBig::from(current_balance) + value.clone(); + if updated_balance < IBig::from(0i32) { + return Err(Error::NegativeTicketBalance); + } + let value_nat: Option = + Option::Some(From::::from(UBig::try_from(updated_balance)?)); + let key_hash = get_ticket_key_hash(tickiter, identifier, identifier_ty, owner); + self.set(format!("/context/tickets/{}", key_hash.value()), value_nat) + .map_err(err_into)?; + + let ticket_balance_diff = TicketBalanceDiff::new( + tickiter.clone(), + identifier.clone(), + identifier_ty.clone(), + owner.clone(), + value, + ); + + self.set_tmp( + format!("/tmp/{}", key_hash.value()), + Option::Some(ticket_balance_diff.into_micheline()), + ) + .map_err(err_into)?; + + Ok(()) + } + + fn aggregate_ticket_updates(&self) -> Vec { todo!() } } diff --git a/michelson_vm/src/typechecker.rs b/michelson_vm/src/typechecker.rs index 6cc9048..a23a629 100644 --- a/michelson_vm/src/typechecker.rs +++ b/michelson_vm/src/typechecker.rs @@ -130,8 +130,6 @@ pub fn check_types_equal(lhs: &Type, rhs: &Type) -> Result<()> { } } -//pub fn contains_types - impl StackItem { pub fn from_data(data: Data, ty: &Type) -> Result { match ty { diff --git a/michelson_vm/src/types.rs b/michelson_vm/src/types.rs index b789d90..e0ac742 100644 --- a/michelson_vm/src/types.rs +++ b/michelson_vm/src/types.rs @@ -116,7 +116,6 @@ pub enum InternalContent { #[derive(Debug, Clone)] pub struct OperationItem { // domain - //content: InternalContent, destination: Address, param: Box, param_type: Type, diff --git a/michelson_vm/src/types/operation.rs b/michelson_vm/src/types/operation.rs index 13160d2..0954b03 100644 --- a/michelson_vm/src/types/operation.rs +++ b/michelson_vm/src/types/operation.rs @@ -13,13 +13,6 @@ use crate::Result; use super::{MutezItem, StackItem}; impl OperationItem { - // pub fn new(content: InternalContent) -> Self { - // Self { - // content, - // big_map_diff: Vec::new(), - // } - // } - pub fn new( destination: Address, param: StackItem, @@ -28,11 +21,11 @@ impl OperationItem { source: ImplicitAddress, ) -> Self { Self { - destination: destination, + destination, param: Box::new(param), - param_type: param_type, - amount: amount, - source: source, + param_type, + amount, + source, big_map_diff: Vec::new(), } } diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 67da5f8..3199666 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -4,21 +4,26 @@ use std::fmt::Display; -use tezos_michelson::michelson::types::{self, Type}; +use ibig::IBig; +use tezos_core::types::encoded::Address; +use tezos_michelson::{ + micheline::Micheline, + michelson::types::{self, Type}, +}; use crate::{interpreter::TicketStorage, types::TicketItem, Result}; use super::{ - AddressItem, BigMapItem, ListItem, MapItem, NatItem, OperationItem, OptionItem, OrItem, - PairItem, StackItem, + AddressItem, BigMapDiff, BigMapItem, ListItem, MapItem, NatItem, OperationItem, OptionItem, + OrItem, PairItem, StackItem, }; impl TicketItem { pub fn new(source: AddressItem, identifier: StackItem, amount: NatItem) -> Self { Self { - source: source, + source, identifier: Box::new(identifier), - amount: amount, + amount, } } @@ -79,7 +84,7 @@ impl TicketStorage for TicketItem { impl TicketStorage for BigMapItem { fn has_tickets(&self) -> bool { match self { - BigMapItem::Diff(val) => todo!(), + BigMapItem::Diff(val) => val.has_tickets(), BigMapItem::Map(val) => val.has_tickets(), BigMapItem::Ptr(_) => false, } @@ -87,13 +92,46 @@ impl TicketStorage for BigMapItem { fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { match self { - BigMapItem::Diff(val) => todo!(), + BigMapItem::Diff(val) => val.iter_tickets(action), BigMapItem::Map(val) => val.iter_tickets(action), BigMapItem::Ptr(_) => Ok(()), } } } +impl TicketStorage for BigMapDiff { + fn has_tickets(&self) -> bool { + for (_key_hash, (key, value)) in &self.updates { + let key_item = StackItem::from_micheline(key.clone(), &self.inner_type.0).unwrap(); + if key_item.has_tickets() { + return true; + } + if let Some(value_micheline) = value { + let value_item = + StackItem::from_micheline(value_micheline.clone(), &self.inner_type.1).unwrap(); + if value_item.has_tickets() { + return true; + } + } + } + false + } + + fn iter_tickets(&self, action: &mut impl FnMut(&TicketItem) -> Result<()>) -> Result<()> { + for (_key_hash, (key, value_opt)) in &self.updates { + let key_item = StackItem::from_micheline(key.clone(), &self.inner_type.0).unwrap(); + key_item.iter_tickets(action)?; + + if let Some(value) = value_opt { + let value_item = + StackItem::from_micheline(value.clone(), &self.inner_type.1).unwrap(); + value_item.iter_tickets(action)?; + } + } + Ok(()) + } +} + impl TicketStorage for OptionItem { fn has_tickets(&self) -> bool { match self { @@ -185,3 +223,32 @@ impl TicketStorage for OperationItem { self.param.iter_tickets(action) } } + +pub struct TicketBalanceDiff { + tickiter: Address, + identifier: Micheline, + identifier_ty: Type, + owner: Address, + value: IBig, +} + +impl TicketBalanceDiff { + pub fn new( + tickiter: Address, + identifier: Micheline, + identifier_ty: Type, + owner: Address, + value: IBig, + ) -> Self { + TicketBalanceDiff { + tickiter, + identifier, + identifier_ty, + owner, + value, + } + } + pub fn into_micheline(&self) -> Micheline { + todo!() + } +} From aea7a2cd8cbbc5a30bc3314dbcd9cb4c9183014b Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Sun, 20 Aug 2023 22:19:44 +0300 Subject: [PATCH 6/8] Move ticket balance updates to tmp state --- layered_store/src/store.rs | 24 +++++++++++++++++++----- michelson_vm/src/interpreter.rs | 2 +- michelson_vm/src/mock.rs | 2 +- michelson_vm/src/store.rs | 8 ++++++-- michelson_vm/src/types/ticket.rs | 5 +++++ 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/layered_store/src/store.rs b/layered_store/src/store.rs index 7c0681a..d2c3edf 100644 --- a/layered_store/src/store.rs +++ b/layered_store/src/store.rs @@ -42,7 +42,7 @@ pub struct LayeredStore { backend: Backend, pending_state: HashMap>, modified_keys: HashSet, - //tmp_state: HashMap>, + tmp_state: HashMap>, } impl LayeredStore { @@ -51,7 +51,7 @@ impl LayeredStore { backend, pending_state: HashMap::new(), modified_keys: HashSet::new(), - //tmp_state: Hash + tmp_state: HashMap::new(), } } @@ -111,14 +111,26 @@ impl LayeredStore { pub fn set_tmp(&mut self, key: String, val: Option) -> Result<()> { match val { Some(value) => self - .pending_state + .tmp_state .insert(key.clone(), Some((Box::new(value), Box::new(T::serialize)))), - None => self.pending_state.insert(key.clone(), None), + None => self.tmp_state.insert(key.clone(), None), }; Ok(()) } - //pub fn iter_pending(&mut self, key_prefix: String, ) + pub fn pop_tmp(&mut self) -> Result> { + let values: Vec = self + .tmp_state + .drain() + .filter(|v| v.1.is_some()) + .map(|v| { + let (dyn_value, _) = v.1.expect("Value must be not None"); + T::downcast_ref(&dyn_value).unwrap().clone() + }) + .collect(); + + Ok(values) + } pub fn commit(&mut self) -> Result<()> { let modified_keys: Vec = self.modified_keys.drain().collect(); @@ -145,12 +157,14 @@ impl LayeredStore { for key in self.modified_keys.drain().into_iter() { self.pending_state.remove(&key); } + //self.tmp_state.clear(); } pub fn clear(&mut self) { self.pending_state.clear(); self.modified_keys.clear(); self.backend.clear(); + self.tmp_state.clear(); } } diff --git a/michelson_vm/src/interpreter.rs b/michelson_vm/src/interpreter.rs index ad091a7..98174f8 100644 --- a/michelson_vm/src/interpreter.rs +++ b/michelson_vm/src/interpreter.rs @@ -52,7 +52,7 @@ pub trait InterpreterContext { owner: &Address, value: IBig, ) -> Result<()>; - fn aggregate_ticket_updates(&self) -> Vec; + fn aggregate_ticket_updates(&mut self) -> Vec; } pub struct OperationScope { diff --git a/michelson_vm/src/mock.rs b/michelson_vm/src/mock.rs index 479f268..95ef8aa 100644 --- a/michelson_vm/src/mock.rs +++ b/michelson_vm/src/mock.rs @@ -169,7 +169,7 @@ impl InterpreterContext for MockContext { todo!() } - fn aggregate_ticket_updates(&self) -> Vec { + fn aggregate_ticket_updates(&mut self) -> Vec { todo!() } } diff --git a/michelson_vm/src/store.rs b/michelson_vm/src/store.rs index e78c8e3..a180f4d 100644 --- a/michelson_vm/src/store.rs +++ b/michelson_vm/src/store.rs @@ -147,7 +147,11 @@ impl InterpreterContext for LayeredStore { Ok(()) } - fn aggregate_ticket_updates(&self) -> Vec { - todo!() + fn aggregate_ticket_updates(&mut self) -> Vec { + self.pop_tmp::() + .unwrap() + .iter() + .map(|v| TicketBalanceDiff::from_micheline(v)) + .collect() } } diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 3199666..9646771 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -248,7 +248,12 @@ impl TicketBalanceDiff { value, } } + pub fn into_micheline(&self) -> Micheline { todo!() } + + pub fn from_micheline(micheline: &Micheline) -> Self { + todo!() + } } From b6ad80ed3c5c044522bb61480e978dfc74a7a048 Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Sun, 20 Aug 2023 23:03:30 +0300 Subject: [PATCH 7/8] Implement tickets methods for MockContext --- michelson_vm/src/mock.rs | 44 +++++++++++++++++++++++++++----- michelson_vm/src/script.rs | 6 +++++ michelson_vm/src/store.rs | 15 ++++------- michelson_vm/src/types/ticket.rs | 12 ++++++++- 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/michelson_vm/src/mock.rs b/michelson_vm/src/mock.rs index 95ef8aa..5b69080 100644 --- a/michelson_vm/src/mock.rs +++ b/michelson_vm/src/mock.rs @@ -2,11 +2,12 @@ // // SPDX-License-Identifier: MIT -use ibig::IBig; +use ibig::{IBig, UBig}; use std::collections::HashMap; use tezos_core::types::{ encoded::{self, Address, Encoded}, mutez::Mutez, + number::Nat, }; use tezos_michelson::michelson::types::unit; use tezos_michelson::{ @@ -17,8 +18,8 @@ use tezos_michelson::{ use crate::{ interpreter::{InterpreterContext, OperationScope}, trace_log, - types::ticket::TicketBalanceDiff, - Result, + types::ticket::{get_ticket_key_hash, TicketBalanceDiff}, + Error, Result, }; pub const CHAIN_ID: &str = "NetXP2FfcNxFANL"; @@ -48,6 +49,8 @@ pub struct MockContext { pub big_map_values: HashMap<(i64, String), Micheline>, pub contracts: HashMap, pub balances: HashMap, + pub ticket_balances: HashMap, + pub ticket_balance_diffs: Vec, } impl MockContext { @@ -58,6 +61,8 @@ impl MockContext { big_map_values: HashMap::new(), contracts: HashMap::new(), balances: HashMap::new(), + ticket_balances: HashMap::new(), + ticket_balance_diffs: vec![], } } } @@ -155,7 +160,12 @@ impl InterpreterContext for MockContext { identifier_ty: &Type, owner: &Address, ) -> Result { - todo!() + let key_hash = get_ticket_key_hash(tickiter, identifier, identifier_ty, owner); + + match self.ticket_balances.get(key_hash.value()) { + Some(balance) => Ok(UBig::from(balance.clone())), + None => Ok(UBig::from(0u32)), + } } fn update_ticket_balance( @@ -166,10 +176,32 @@ impl InterpreterContext for MockContext { owner: &Address, value: IBig, ) -> Result<()> { - todo!() + let current_balance: UBig = + self.get_ticket_balance(tickiter, identifier, identifier_ty, owner)?; + let updated_balance: IBig = IBig::from(current_balance) + value.clone(); + if updated_balance < IBig::from(0i32) { + return Err(Error::NegativeTicketBalance); + } + let value_nat: Nat = From::::from(UBig::try_from(updated_balance)?); + let key_hash = get_ticket_key_hash(tickiter, identifier, identifier_ty, owner); + + self.ticket_balances + .insert(key_hash.into_string(), value_nat); + + let ticket_balance_diff = TicketBalanceDiff::new( + tickiter.clone(), + identifier.clone(), + identifier_ty.clone(), + owner.clone(), + value, + ); + + self.ticket_balance_diffs.push(ticket_balance_diff); + + Ok(()) } fn aggregate_ticket_updates(&mut self) -> Vec { - todo!() + self.ticket_balance_diffs.clone() } } diff --git a/michelson_vm/src/script.rs b/michelson_vm/src/script.rs index 03b7b9e..bd655a5 100644 --- a/michelson_vm/src/script.rs +++ b/michelson_vm/src/script.rs @@ -11,6 +11,7 @@ use tezos_michelson::michelson::{ }; use crate::interpreter::TicketStorage; +use crate::types::ticket::TicketBalanceDiff; use crate::{ entrypoints::normalize_parameter, err_mismatch, err_unsupported, internal_error, @@ -33,6 +34,7 @@ pub struct ScriptReturn { pub storage: Micheline, pub operations: Vec, pub big_map_diff: Vec, + pub ticket_balance_diff: Vec, } impl MichelsonScript { @@ -122,10 +124,13 @@ impl MichelsonScript { } } + let ticket_balance_updates = context.aggregate_ticket_updates(); + let ret = ScriptReturn { big_map_diff, operations, storage: storage.into_micheline(&self.storage_type)?, + ticket_balance_diff: ticket_balance_updates, }; Ok(ret) } @@ -174,6 +179,7 @@ impl MichelsonScript { big_map_diff, storage: storage.into_micheline(&self.storage_type)?, operations: vec![], + ticket_balance_diff: vec![], }; Ok(ret) } diff --git a/michelson_vm/src/store.rs b/michelson_vm/src/store.rs index a180f4d..5227edc 100644 --- a/michelson_vm/src/store.rs +++ b/michelson_vm/src/store.rs @@ -10,16 +10,11 @@ use tezos_core::types::{ }; use tezos_michelson::{micheline::Micheline, michelson::types::Type}; -use crate::{error::err_into, types::ticket::TicketBalanceDiff, Error, InterpreterContext, Result}; - -fn get_ticket_key_hash( - tickiter: &Address, - identifier: &Micheline, - identifier_ty: &Type, - owner: &Address, -) -> ScriptExprHash { - todo!() -} +use crate::{ + error::err_into, + types::ticket::{get_ticket_key_hash, TicketBalanceDiff}, + Error, InterpreterContext, Result, +}; impl InterpreterContext for LayeredStore { fn set_contract_type(&mut self, address: ContractAddress, value: Micheline) -> Result<()> { diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 9646771..64df2b3 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -5,7 +5,7 @@ use std::fmt::Display; use ibig::IBig; -use tezos_core::types::encoded::Address; +use tezos_core::types::encoded::{Address, ScriptExprHash}; use tezos_michelson::{ micheline::Micheline, michelson::types::{self, Type}, @@ -224,6 +224,7 @@ impl TicketStorage for OperationItem { } } +#[derive(Clone, Debug)] pub struct TicketBalanceDiff { tickiter: Address, identifier: Micheline, @@ -257,3 +258,12 @@ impl TicketBalanceDiff { todo!() } } + +pub(crate) fn get_ticket_key_hash( + tickiter: &Address, + identifier: &Micheline, + identifier_ty: &Type, + owner: &Address, +) -> ScriptExprHash { + todo!() +} From 4c63c0f08570e658776ab3c7776c67cfa81095af Mon Sep 17 00:00:00 2001 From: Igor Matsak Date: Mon, 21 Aug 2023 01:01:55 +0300 Subject: [PATCH 8/8] Implement TicketBalanceDiff --- michelson_vm/src/store.rs | 2 +- michelson_vm/src/types/ticket.rs | 96 +++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/michelson_vm/src/store.rs b/michelson_vm/src/store.rs index 5227edc..ca616a7 100644 --- a/michelson_vm/src/store.rs +++ b/michelson_vm/src/store.rs @@ -146,7 +146,7 @@ impl InterpreterContext for LayeredStore { self.pop_tmp::() .unwrap() .iter() - .map(|v| TicketBalanceDiff::from_micheline(v)) + .map(|v| TicketBalanceDiff::from_micheline(v).unwrap()) .collect() } } diff --git a/michelson_vm/src/types/ticket.rs b/michelson_vm/src/types/ticket.rs index 64df2b3..f7b2047 100644 --- a/michelson_vm/src/types/ticket.rs +++ b/michelson_vm/src/types/ticket.rs @@ -2,16 +2,22 @@ // // SPDX-License-Identifier: MIT -use std::fmt::Display; +use std::{fmt::Display, str::FromStr}; use ibig::IBig; -use tezos_core::types::encoded::{Address, ScriptExprHash}; +use tezos_core::{ + internal::crypto::blake2b, + types::encoded::{Address, Encoded, ScriptExprHash}, +}; use tezos_michelson::{ micheline::Micheline, - michelson::types::{self, Type}, + michelson::{ + data::{self, Data}, + types::{self, Type}, + }, }; -use crate::{interpreter::TicketStorage, types::TicketItem, Result}; +use crate::{err_mismatch, interpreter::TicketStorage, types::TicketItem, Result}; use super::{ AddressItem, BigMapDiff, BigMapItem, ListItem, MapItem, NatItem, OperationItem, OptionItem, @@ -251,11 +257,64 @@ impl TicketBalanceDiff { } pub fn into_micheline(&self) -> Micheline { - todo!() + let tickiter = Micheline::from(Data::String( + data::String::from_string(self.tickiter.value().to_string()).unwrap(), + )); + let identifier_ty = Micheline::try_from(self.identifier_ty.clone()).unwrap(); + let owner = Micheline::from(Data::String( + data::String::from_string(self.owner.value().to_string()).unwrap(), + )); + let value = Micheline::from(Data::String( + data::String::from_string(format!("{}", self.value)).unwrap(), + )); + + let vec = vec![ + tickiter, + self.identifier.clone(), + identifier_ty, + owner, + value, + ]; + + vec.into() } - pub fn from_micheline(micheline: &Micheline) -> Self { - todo!() + pub fn from_micheline(micheline: &Micheline) -> Result { + match micheline { + Micheline::Literal(_) => err_mismatch!("Sequence", "Literal"), + Micheline::PrimitiveApplication(_) => err_mismatch!("Sequence", "PrimitiveApplication"), + Micheline::Sequence(seq) => Ok(TicketBalanceDiff { + tickiter: Address::new( + seq.values()[0] + .clone() + .into_literal() + .unwrap() + .into_micheline_string() + .unwrap() + .into_string(), + )?, + identifier: seq.values()[1].clone(), + identifier_ty: Type::try_from(seq.values()[2].clone())?, + owner: Address::new( + seq.values()[3] + .clone() + .into_literal() + .unwrap() + .into_micheline_string() + .unwrap() + .into_string(), + )?, + value: IBig::from_str( + seq.values()[4] + .clone() + .into_literal() + .unwrap() + .into_micheline_string() + .unwrap() + .to_str(), + )?, + }), + } } } @@ -265,5 +324,26 @@ pub(crate) fn get_ticket_key_hash( identifier_ty: &Type, owner: &Address, ) -> ScriptExprHash { - todo!() + let vec = vec![ + Micheline::from(Data::String( + data::String::from_string(tickiter.value().to_string()).unwrap(), + )), + Micheline::try_from(identifier_ty.clone()).unwrap(), + identifier.clone(), + Micheline::from(Data::String( + data::String::from_string(owner.value().to_string()).unwrap(), + )), + ]; + + let expr = Micheline::from(vec); + // let ty = types::Pair::new(vec![ + // types::Address::new(None).into(), + // identifier_ty.clone(), + // types::Address::new(None).into(), + // ], None); + + //let schema: Micheline = Michelson::from(ty.clone()).into(); + let payload = expr.pack(None).unwrap(); + let hash = blake2b(payload.as_slice(), 32).unwrap(); + ScriptExprHash::from_bytes(hash.as_slice()).unwrap() }