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
419 changes: 195 additions & 224 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build/operator/Dockerfile.local
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FROM alpine:3.15 AS rollup
RUN apk --no-cache add binutils gcc gmp libgmpxx hidapi libc-dev libev libffi sudo
ARG OCTEZ_PROTO
COPY --from=octez /usr/local/bin/octez-smart-rollup-node-${OCTEZ_PROTO} /usr/bin/octez-smart-rollup-node
COPY --from=octez /usr/local/bin/octez-smart-rollup-client-${OCTEZ_PROTO} /usr/bin/octez-smart-rollup-client
COPY --from=octez /usr/local/bin/octez-client /usr/bin/octez-client
COPY ./bin/wasm_2_0_0/ /root/wasm_2_0_0/
ARG PACKAGE
Expand Down
7 changes: 5 additions & 2 deletions layered_store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ crate-type = ["cdylib", "rlib"]
derive_more = "0.99"
tezos_core = { git = "https://github.com/baking-bad/tezos-rust-sdk", optional = true, branch = "develop", package = "tezos-core", default-features = false, features = ["ed25519"] }
tezos_michelson = { git = "https://github.com/baking-bad/tezos-rust-sdk", optional = true, branch = "develop", package = "tezos-michelson", default-features = false }
tezos_rpc = { git = "https://github.com/baking-bad/tezos-rust-sdk", optional = true, branch = "develop", package = "tezos-rpc", default-features = false }
tezos_operation = { git = "https://github.com/baking-bad/tezos-rust-sdk", optional = true, branch = "develop", package = "tezos-operation", default-features = false }
serde-json-wasm = { git = "https://github.com/CosmWasm/serde-json-wasm", optional = true, branch = "main" }

[features]
default = []
default = ["tezos"]
testing = ["tezos"]
tezos = ["dep:tezos_core", "dep:tezos_michelson"]
tezos = ["dep:tezos_core", "dep:tezos_michelson", "dep:tezos_rpc", "dep:tezos_operation", "dep:serde-json-wasm"]
1 change: 1 addition & 0 deletions layered_store/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum Error {
Internal(InternalError),
ContextUnstagedError,
DowncastingError,
InvalidBytes,
}

pub type Result<T> = std::result::Result<T, Error>;
Expand Down
4 changes: 4 additions & 0 deletions layered_store/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ impl<Backend: StoreBackend> LayeredStore<Backend> {
Ok(())
}

// TODO: implement state hash calculation
// state = blake2b(state, key, value)
// accept prev state as an optional argument
// if state is None skip hashing
pub fn commit(&mut self) -> Result<()> {
let modified_keys: Vec<String> = self.modified_keys.drain().collect();
for key in modified_keys {
Expand Down
29 changes: 28 additions & 1 deletion layered_store/src/types/tezos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use tezos_core::types::{
number::Nat,
};
use tezos_michelson::micheline::Micheline;
use tezos_operation::operations::SignedOperation;
use tezos_rpc::models::operation::Operation;

use crate::{internal_error, Result, StoreType};
use crate::{error::err_into, internal_error, Result, StoreType};

macro_rules! impl_for_core {
($cls: ident, $ty: ty) => {
Expand All @@ -31,3 +33,28 @@ impl_for_core!(Encoded, ContractAddress);
impl_for_core!(Micheline, Micheline);
impl_for_core!(Mutez, Mutez);
impl_for_core!(Nat, Nat);

impl StoreType for Operation {
fn from_bytes(_bytes: &[u8]) -> Result<Self> {
#[cfg(not(target_arch = "wasm32"))]
{
Ok(serde_json_wasm::de::from_slice(_bytes).map_err(err_into)?)
}
#[cfg(target_arch = "wasm32")]
unimplemented!()
}

fn to_bytes(&self) -> Result<Vec<u8>> {
serde_json_wasm::ser::to_vec(&self).map_err(err_into)
}
}

impl StoreType for SignedOperation {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
SignedOperation::from_bytes(bytes).map_err(err_into)
}

fn to_bytes(&self) -> Result<Vec<u8>> {
SignedOperation::to_bytes(&self).map_err(err_into)
}
}
6 changes: 2 additions & 4 deletions michelson_vm/src/instructions/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//
// SPDX-License-Identifier: MIT

use std::borrow::Borrow;

use tezos_michelson::michelson::data::instructions::{Dig, Drop, Dug, Dup, Push, Swap};

use crate::{
Expand Down Expand Up @@ -56,14 +54,14 @@ impl PureInterpreter for Swap {

impl PureInterpreter for Dig {
fn execute(&self, stack: &mut Stack) -> Result<()> {
let item = stack.pop_at(self.n.borrow().try_into()?)?;
let item = stack.pop_at(self.n.clone().try_into()?)?;
stack.push(item)
}
}

impl PureInterpreter for Dug {
fn execute(&self, stack: &mut Stack) -> Result<()> {
let item = stack.pop()?;
stack.push_at(self.n.borrow().try_into()?, item)
stack.push_at(self.n.clone().try_into()?, item)
}
}
4 changes: 2 additions & 2 deletions sapling_proto/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ impl TryInto<Vec<u8>> for &Ciphertext {

#[cfg(test)]
mod test {
use std::{borrow::Borrow, io};
use std::io;

use crate::{storage::Ciphertext, types::SaplingTransaction};

Expand All @@ -238,7 +238,7 @@ mod test {
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;

let ciphertext = Ciphertext::try_from(payload.as_slice())?;
let res: Vec<u8> = ciphertext.borrow().try_into()?;
let res: Vec<u8> = (&ciphertext).try_into()?;

assert_eq!(payload, res);
Ok(())
Expand Down
1 change: 1 addition & 0 deletions tezos_kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ crate-type = ["cdylib", "rlib"]
derive_more = "0.99"
kernel_io = { path = "../kernel_io" }
tezos_proto = { path = "../tezos_proto" }
layered_store = { path = "../layered_store", features = ["tezos"], default-features = false }
tezos_core = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-core", default-features = false }
tezos_operation = { git = "https://github.com/baking-bad/tezos-rust-sdk", branch = "develop", package = "tezos-operation", default-features = false }
tezos-smart-rollup-host = { version = "0.2.0", features = ["proto-nairobi"], default-features = false }
Expand Down
3 changes: 3 additions & 0 deletions tezos_kernel/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ impl_from_error!(tezos_operation::Error);
impl_from_error!(tezos_core::Error);
impl_from_error!(tezos_smart_rollup_host::runtime::RuntimeError);
impl_from_error!(tezos_smart_rollup_host::path::PathError);
impl_from_error!(kernel_io::Error);
impl_from_error!(layered_store::Error);


impl From<tezos_proto::Error> for Error {
fn from(error: tezos_proto::Error) -> Self {
Expand Down
130 changes: 74 additions & 56 deletions tezos_kernel/src/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,97 @@
// SPDX-License-Identifier: MIT

use kernel_io::{
inbox::{read_inbox, InboxMessage},
inbox::{read_inbox, InboxMessage, PayloadType},
KernelStore, KernelStoreAsHost,
};
use tezos_core::types::encoded::{ChainId, Encoded, OperationHash};
use tezos_core::types::encoded::{ChainId, Encoded};
use tezos_proto::{batch::payload::BatchPayload, protocol::{self, Protocol}};
use tezos_operation::operations::SignedOperation;
use tezos_proto::{batcher::apply_batch, context::TezosContext};
use tezos_smart_rollup_core::SmartRollupCore;
use tezos_smart_rollup_host::runtime::Runtime;

use crate::{payload::TezosPayload, Error, Result};
use crate::Result;

pub enum TezosPayload {
Operation(SignedOperation),
Batch(BatchPayload),
}

impl PayloadType for TezosPayload {
fn from_external_message(message: &[u8]) -> kernel_io::Result<Self> {
let opg = SignedOperation::from_bytes(message).map_err(err_into)?;
Ok(TezosPayload::Operation(opg))
}
}

pub fn kernel_dispatch<Host: SmartRollupCore, Proto: Protocol>(context: &mut KernelStore<Host>, prefix: &[u8]) -> Result<(bool, bool)> {
match read_inbox(context.as_host(), prefix) {
Ok(InboxMessage::BeginBlock(_)) => {
let chain_id = ChainId::from_bytes(&prefix[..4])
.expect("Failed to decode chain ID");
let init = protocol::initialize::<Proto>(context, chain_id)?;
Ok((init, init))
},
Ok(InboxMessage::LevelInfo(info)) => {
context.set("/time".into(), Some(info.predecessor_timestamp))?;
context.commit()?;
Ok((true, false))
}
Ok(InboxMessage::Payload(TezosPayload::Operation(operation))) => {
protocol::inject_operation::<Proto>(context, operation)?;
context.as_host().mark_for_reboot()?;
Ok((true, true))
}
Ok(InboxMessage::Payload(TezosPayload::Batch(batch))) => {
protocol::inject_batch::<Proto>(context, batch)?;
context.as_host().mark_for_reboot()?;
Ok((true, true))
}
Ok(InboxMessage::EndBlock(_)) => {
let timestamp: i64 = context.get("/time".into())?.unwrap_or(0i64);
protocol::finalize::<Proto>(context, timestamp)?;
Ok((true, true))
}
Ok(InboxMessage::NoMoreData) => Ok((false, true)),
Ok(InboxMessage::Foreign(id)) => {
context.log(format!("Foreign message #{}", id));
Ok((false, false))
},
Ok(InboxMessage::Unknown(id)) => {
context.log(format!("Unknown message #{}", id));
Ok((false, false))
},
Err(err) => Err(err.into())
}
}

pub fn kernel_run<Host: SmartRollupCore>(host: &mut Host) {
let mut context = KernelStore::attach(host);
context.log(format!("Kernel boots"));
context.clear();

let metadata = Runtime::reveal_metadata(context.as_host());
let mut head = context.get_head().expect("Failed to get head");
head.chain_id =
ChainId::from_bytes(&metadata.raw_rollup_address[..4]).expect("Failed to decode chain ID");

context.log(format!("Kernel invoked, prev head: {}", head));

let mut batch_payload: Vec<(OperationHash, SignedOperation)> = Vec::new();
let res: Result<()> = loop {
match read_inbox(context.as_host(), &metadata.raw_rollup_address[..4]) {
Ok(InboxMessage::BeginBlock(inbox_level)) => {
// Origination level is the one before kernel is first time invoked
// We assume single rollup block per inbox block here
// Note that head level is the one prior to the current block
let expected = inbox_level - metadata.origination_level as i32 - 2;
if head.level != expected {
break Err(Error::InconsistentHeadLevel {
expected,
found: head.level,
});
loop {
match kernel_dispatch::<Host, DefaultConfig>(&mut context, &metadata.raw_rollup_address) {
Ok((save, exit)) => {
if save {
context
.as_mut()
.persist()
.expect("Failed to persist changes");
}
}
Ok(InboxMessage::LevelInfo(info)) => {
head.timestamp = info.predecessor_timestamp;
}
Ok(InboxMessage::Payload(TezosPayload::Operation { hash, opg })) => {
context.log(format!("Operation pending: {}", &hash.value()));
batch_payload.push((hash, opg));
}
Ok(InboxMessage::EndBlock(_)) => {
match apply_batch(&mut context, head.clone(), batch_payload, false) {
Ok(new_head) => {
head = new_head;
context.log(format!("Batch applied: {}", head));
break Ok(());
}
Err(err) => break Err(err.into()),
if exit {
break;
}
},
Err(err) => {
context.clear();
context.log(err.format());
}
Ok(InboxMessage::NoMoreData) => break Ok(()),
Ok(InboxMessage::Foreign(id)) => context.log(format!("Foreign message #{}", id)),
Ok(InboxMessage::Unknown(id)) => context.log(format!("Unknown message #{}", id)),
Err(err) => context.log(err.to_string()),
}
};

match res {
Ok(_) => {
context
.as_mut()
.persist()
.expect("Failed to persist changes");
context.log(format!("Kernel yields"));
}
Err(err) => {
context.log(err.format());
context.clear();
}
}
context.log(format!("Kernel yields"));
}

#[cfg(test)]
Expand Down
3 changes: 2 additions & 1 deletion tezos_kernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

pub mod error;
pub mod kernel;
pub mod payload;
pub mod shell;
pub mod context;

pub use error::{Error, Result};

Expand Down
69 changes: 69 additions & 0 deletions tezos_kernel/src/mempool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::mem::size_of;

use layered_store::StoreType;
use tezos_core::types::encoded::OperationHash;
use tezos_operation::operations::SignedOperation;

use crate::Result;

#[derive(Debug, Clone, Default)]
pub struct MempoolState(Vec<u8>);

impl StoreType for MempoolState {
fn to_bytes(&self) -> layered_store::Result<Vec<u8>> {
Ok(self.0.clone())
}

fn from_bytes(bytes: &[u8]) -> layered_store::Result<Self> {
Ok(Self(bytes.to_vec()))
}
}

impl MempoolState {
const HASH_SIZE: usize = 32;

pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn len(&self) -> usize {
self.0.len() / Self::HASH_SIZE
}

fn find(&self, term: &[u8]) -> Option<usize> {
let mut ptr = 0;
while ptr + Self::HASH_SIZE <= self.0.len() {
if &self.0[ptr..ptr+Self::HASH_SIZE] == term {
return Some(ptr);
}
ptr += Self::HASH_SIZE;
}
None
}

pub fn add(&mut self, hash: &OperationHash) -> Result<bool> {
let mut term = StoreType::to_bytes(hash)?;
if self.find(term.as_slice()).is_none() {
self.0.append(&mut term);
return Ok(true);
}
Ok(false)
}

pub fn remove(&mut self, hash: &OperationHash) -> Result<bool> {
let term = StoreType::to_bytes(hash)?;
if let Some(ptr) = self.find(term.as_slice()) {
self.0.drain(ptr..ptr+Self::HASH_SIZE);
return Ok(true);
}
Ok(false)
}

pub fn to_vec(&self) -> Result<Vec<OperationHash>> {
let res = self.0
.chunks_exact(Self::HASH_SIZE)
.map(|chunk| StoreType::from_bytes(chunk))
.collect::<layered_store::Result<Vec<OperationHash>>>();
Ok(res?)
}
}
Loading