Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ bin
target
tezos_kernel/target
boot_kernel/target
.env
.env
.bin
local.env
28 changes: 28 additions & 0 deletions layered_store/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub struct LayeredStore<Backend: StoreBackend> {
backend: Backend,
pending_state: HashMap<String, Option<(DynStoreType, StoreTypeSer)>>,
modified_keys: HashSet<String>,
tmp_state: HashMap<String, Option<(DynStoreType, StoreTypeSer)>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just reuse pending_state, the only difference is that for tmp values you do not update modified_keys.
You can also implement that with existing get/set methods by adding flag

}

impl<Backend: StoreBackend> LayeredStore<Backend> {
Expand All @@ -50,6 +51,7 @@ impl<Backend: StoreBackend> LayeredStore<Backend> {
backend,
pending_state: HashMap::new(),
modified_keys: HashSet::new(),
tmp_state: HashMap::new(),
}
}

Expand Down Expand Up @@ -106,6 +108,30 @@ impl<Backend: StoreBackend> LayeredStore<Backend> {
Ok(())
}

pub fn set_tmp<T: StoreType>(&mut self, key: String, val: Option<T>) -> Result<()> {
match val {
Some(value) => self
.tmp_state
.insert(key.clone(), Some((Box::new(value), Box::new(T::serialize)))),
None => self.tmp_state.insert(key.clone(), None),
};
Ok(())
}

pub fn pop_tmp<T: StoreType>(&mut self) -> Result<Vec<T>> {
let values: Vec<T> = 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<String> = self.modified_keys.drain().collect();
for key in modified_keys {
Expand All @@ -131,12 +157,14 @@ impl<Backend: StoreBackend> LayeredStore<Backend> {
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();
}
}

Expand Down
8 changes: 8 additions & 0 deletions michelson_vm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> = std::result::Result<T, Error>;
Expand Down
5 changes: 5 additions & 0 deletions michelson_vm/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down Expand Up @@ -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),
}
}
Expand Down
27 changes: 18 additions & 9 deletions michelson_vm/src/instructions/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,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,
};

Expand Down Expand Up @@ -147,14 +149,21 @@ impl Interpreter for TransferTokens {
// TODO: support big_map ownership transfer
}

let content = InternalContent::Transaction {
destination,
parameter: param.into_micheline(&param_type)?,
amount: amount.try_into()?,
source: scope.source.clone(),
};

let res = OperationItem::new(content);
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(),
&t.identifier.get_type()?,
&scope.self_address.clone().into(),
-amount,
)
})?;

let res = OperationItem::new(destination, param, param_type, amount, scope.source.clone());
stack.push(res.into())
}
}
1 change: 1 addition & 0 deletions michelson_vm/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ mod lambda;
mod math;
mod scope;
mod stack;
mod tickets;
23 changes: 19 additions & 4 deletions michelson_vm/src/instructions/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Error, InterpreterContext, OperationScope, Result,
};

impl PureInterpreter for Push {
Expand All @@ -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(())
}
Expand All @@ -42,6 +52,11 @@ impl PureInterpreter for Dup {
}
// TODO: check if copyable
Copy link
Member

@m-kus m-kus Aug 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invite you to implement this as well :)

let res = stack.dup_at(n - 1)?;

if res.has_tickets() {
return Err(Error::NonDupableType); // proto.alpha.michelson_v1.non_dupable_type
}

stack.push(res)
}
}
Expand Down
127 changes: 127 additions & 0 deletions michelson_vm/src/instructions/tickets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: 2023 Baking Bad <hello@bakingbad.dev>
//
// SPDX-License-Identifier: MIT

use tezos_michelson::michelson::{
data::instructions::{JoinTickets, ReadTicket, SplitTicket, Ticket},
types,
};

use crate::{
err_mismatch,
interpreter::{Interpreter, InterpreterContext, PureInterpreter},
pop_cast,
stack::Stack,
typechecker::check_type_comparable,
types::{AddressItem, OptionItem, PairItem, StackItem, TicketItem},
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)?;

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.clone(),
};

context.update_ticket_balance(
&scope.self_address.clone().into(),
&ticket.identifier.clone().into_micheline(&identifier_ty)?,
&identifier_ty,
&scope.self_address.clone().into(),
amount.value().into(),
)?;

stack.push(StackItem::Option(OptionItem::Some(Box::new(ticket.into()))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is OptionItem::some()

}
}

impl PureInterpreter for ReadTicket {
fn execute(&self, stack: &mut Stack) -> Result<()> {
let ticket = pop_cast!(stack, Ticket);

let pair = PairItem::from_items(vec![
ticket.source.clone().into(),
*ticket.identifier.clone(),
ticket.amount.clone().into(),
])?;

stack.push(ticket.into())?; // return ticket back to stack
stack.push(pair.into())
}
}

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

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)))
}
};

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)));
}

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 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)))
}
};

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)));
}

let ticket = TicketItem {
source: ticket_1.source,
identifier: ticket_1.identifier,
amount: ticket_1.amount + ticket_2.amount,
};

stack.push(StackItem::Option(OptionItem::Some(Box::new(ticket.into()))))
}
}
Loading