diff --git a/manifest/main.ml b/manifest/main.ml index b0de8eaf284a9a1357659f0c2e184fbc03f27682..d0f0a1d104532c8aa69ac59d2fe9ce1e75a90665 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -7843,6 +7843,8 @@ let octez_scoru_sequencer = octez_workers |> open_; octez_smart_rollup_node_lib |> open_; octez_smart_rollup_lib |> open_; + octez_client_base |> open_; + octez_layer2_store |> open_; octez_rpc; octez_rpc_http; octez_rpc_http_server; diff --git a/opam/octez-smart-rollup-sequencer.opam b/opam/octez-smart-rollup-sequencer.opam index cd3b3d5c4c3e359bb82e5c19f540da4111860c2a..3a27e7e32b6178e4c2d72f5e60ecd0271552c908 100644 --- a/opam/octez-smart-rollup-sequencer.opam +++ b/opam/octez-smart-rollup-sequencer.opam @@ -16,6 +16,8 @@ depends: [ "octez-smart-rollup-node-alpha" "octez-smart-rollup-node-lib" "octez-smart-rollup" + "tezos-client-base" + "tezos-layer2-store" ] build: [ ["rm" "-r" "vendors" "contrib"] diff --git a/src/bin_sequencer_node/main_sequencer_node.ml b/src/bin_sequencer_node/main_sequencer_node.ml index a054fe89dd49d34678526be0de6f0ea169012966..27d4cfcd9882e506138c14cf4195ddb040208af1 100644 --- a/src/bin_sequencer_node/main_sequencer_node.ml +++ b/src/bin_sequencer_node/main_sequencer_node.ml @@ -44,9 +44,9 @@ let sc_operator_pkh next = ~desc:"Public key hash, or alias, of a sequencer node operator." ( Tezos_clic.parameter @@ fun cctxt s -> let parse_pkh s = - let from_alias s = Client_keys.Public_key_hash.find cctxt s in + let from_alias s = Client_keys.V0.Public_key_hash.find cctxt s in let from_key s = - match Signature.Public_key_hash.of_b58check_opt s with + match Signature.V0.Public_key_hash.of_b58check_opt s with | None -> failwith "Could not read public key hash for sequencer operator" | Some pkh -> return pkh @@ -58,7 +58,7 @@ let sc_operator_pkh next = match String.split ~limit:1 ':' s with | [_] -> let+ pkh = parse_pkh s in - `Default pkh + `Default (Signature.Of_V0.public_key_hash pkh) | [_purpose; _operator_s] -> failwith "Purposes are not supported for a sequencer operator" | _ -> diff --git a/src/kernel_sequencer/Cargo.lock b/src/kernel_sequencer/Cargo.lock index d45b79b312f5fdfee81f6f8dd919bf0fd20826bb..961f7abb77d4ddce3bc79b9e4ac1bcd330ad10ab 100644 --- a/src/kernel_sequencer/Cargo.lock +++ b/src/kernel_sequencer/Cargo.lock @@ -346,6 +346,7 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" name = "kernel-sequencer" version = "0.1.0" dependencies = [ + "hex", "nom", "tezos-smart-rollup-core", "tezos-smart-rollup-debug", diff --git a/src/kernel_sequencer/Cargo.toml b/src/kernel_sequencer/Cargo.toml index 5e8c913cd9a3057b682504c325e785a92e1586a4..b22f0130648eb9428c7ceabb2fcb9e915da00fd2 100644 --- a/src/kernel_sequencer/Cargo.toml +++ b/src/kernel_sequencer/Cargo.toml @@ -46,10 +46,16 @@ version = "0.2.0" path = "../kernel_sdk/debug" version = "0.2.0" +[dependencies.hex] +version = "0.4" + [dev-dependencies.tezos_crypto_rs] version = "0.5.0" default-features = false +[dev-dependencies.hex] +version = "0.4.3" + [features] default = [] diff --git a/src/kernel_sequencer/src/delayed_inbox.rs b/src/kernel_sequencer/src/delayed_inbox.rs index 252fd41cbf975cb0102e7865b6b38353ba60f2c7..af36523d9a5019d2317ddc4722b607da9f03a52b 100644 --- a/src/kernel_sequencer/src/delayed_inbox.rs +++ b/src/kernel_sequencer/src/delayed_inbox.rs @@ -3,10 +3,10 @@ // // SPDX-License-Identifier: MIT +use tezos_crypto_rs::PublicKeySignatureVerifier; use tezos_data_encoding::enc::BinWriter; use tezos_data_encoding::nom::NomReader; use tezos_smart_rollup_debug::debug_msg; -use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; use tezos_smart_rollup_host::{ input::Message, metadata::{RollupMetadata, RAW_ROLLUP_ADDRESS_SIZE}, @@ -14,7 +14,7 @@ use tezos_smart_rollup_host::{ }; use crate::{ - message::{Framed, KernelMessage, Sequence, SequencerMsg, SetSequencer}, + message::{Framed, KernelMessage, Sequence, SequencerMsg, SetSequencer, Signed}, queue::Queue, routing::FilterBehavior, state::{update_state, State}, @@ -61,24 +61,22 @@ pub fn read_input( match message { Err(_) => {} Ok((_, message)) => match message { - KernelMessage::Sequencer(Framed { - destination, - payload: SequencerMsg::Sequence(sequence), - }) => handle_sequence_message( - host, - sequence, - destination, - &raw_rollup_address, - state, - ), - KernelMessage::Sequencer(Framed { - destination, - payload: SequencerMsg::SetSequencer(set_sequence), - }) => handle_set_sequencer_message( - set_sequence, - destination, - &raw_rollup_address, - ), + KernelMessage::Sequencer(sequencer_msg) => { + let Ok(payload) = extract_payload( + sequencer_msg, + &raw_rollup_address, + state, + ) else { continue;}; + + match payload { + SequencerMsg::Sequence(sequence) => { + handle_sequence_message(host, sequence) + } + SequencerMsg::SetSequencer(set_sequencer) => { + handle_set_sequencer_message(set_sequencer) + } + } + } KernelMessage::DelayedMessage(user_message) => { let _ = handle_message( host, @@ -97,37 +95,60 @@ pub fn read_input( } } -/// Handle Sequence message -fn handle_sequence_message( - host: &impl Runtime, - sequence: Sequence, - destination: SmartRollupAddress, +/// Extracts the payload of the message sent by the sequencer. +/// +/// The destination has to match the current rollup address. +/// The state of the kernel has to be `Sequenced`. +/// The signature has to be valid. +fn extract_payload( + sequencer_msg: Signed>, rollup_address: &[u8; RAW_ROLLUP_ADDRESS_SIZE], state: State, -) { +) -> Result { + let Signed { + body: Framed { + destination, + payload: _, + }, + signature, + } = &sequencer_msg; + + // Verify if the destination is for this rollup. if destination.hash().as_ref() != rollup_address { - return; + return Err(RuntimeError::HostErr( + tezos_smart_rollup_host::Error::GenericInvalidAccess, + )); } - debug_msg!( - host, - "Received a sequence message {:?} targeting our rollup", - sequence - ); + // Check if state is sequenced. + let State::Sequenced(sequencer_address) = state else { + return Err(RuntimeError::HostErr( + tezos_smart_rollup_host::Error::GenericInvalidAccess, + )); + }; - let State::Sequenced(_) = state else {return;}; + // Verify the signature of the message. + let hash = sequencer_msg.hash()?; + let signature_is_correct = sequencer_address + .verify_signature(signature, hash.as_ref()) + .map_err(|_| RuntimeError::HostErr(tezos_smart_rollup_host::Error::GenericInvalidAccess))?; + if !signature_is_correct { + return Err(RuntimeError::HostErr( + tezos_smart_rollup_host::Error::GenericInvalidAccess, + )); + } + + Ok(sequencer_msg.body.payload) +} +/// Handle Sequence message +fn handle_sequence_message(host: &H, sequence: Sequence) { // process the sequence + debug_msg!(host, "Received {:?} targeting our rollup", sequence); } -fn handle_set_sequencer_message( - _set_sequencer: SetSequencer, - destination: SmartRollupAddress, - rollup_address: &[u8; RAW_ROLLUP_ADDRESS_SIZE], -) { - if destination.hash().as_ref() == rollup_address { - // process the set sequencer message - } +fn handle_set_sequencer_message(_set_sequencer: SetSequencer) { + // process the set sequencer message } /// Handle messages @@ -144,7 +165,7 @@ fn handle_message( if filter_behavior.predicate(user_message.as_ref(), rollup_address) { debug_msg!( host, - "Received user message {:?} targeting our rollup, hence, will be added to the delayed inbox", + "Received a user message {:?} targeting our rollup, hence, will be added to the delayed inbox", user_message ); diff --git a/src/kernel_sequencer/src/message.rs b/src/kernel_sequencer/src/message.rs index 8f5f85825d9dace34e302f04add0086cd2ec7b59..a505896cb0c3a7e8575dd1f67e841d2c7e191bd9 100644 --- a/src/kernel_sequencer/src/message.rs +++ b/src/kernel_sequencer/src/message.rs @@ -8,13 +8,40 @@ use nom::{ combinator::{all_consuming, map}, sequence::preceded, }; -use tezos_crypto_rs::hash::Signature; +use tezos_crypto_rs::{blake2b, hash::Signature}; use tezos_data_encoding::{ enc::{self, BinResult, BinWriter}, nom::{NomReader, NomResult}, }; use tezos_smart_rollup_encoding::public_key::PublicKey; use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; +use tezos_smart_rollup_host::runtime::RuntimeError; + +#[derive(Debug, Clone, PartialEq, Eq, BinWriter, NomReader)] +pub struct Signed +where + A: NomReader + BinWriter, +{ + pub body: A, + pub signature: Signature, +} + +impl Signed +where + A: NomReader + BinWriter, +{ + /// Returns the hash of the message + pub fn hash(&self) -> Result, RuntimeError> { + // Get the bytes of the body. + let mut bytes = Vec::new(); + self.body + .bin_write(&mut bytes) + .map_err(|_| RuntimeError::DecodingError)?; + + // Returns the hash of the body. + blake2b::digest_256(&bytes).map_err(|_| RuntimeError::DecodingError) + } +} /// Framing protocol v0 /// @@ -58,7 +85,6 @@ pub struct Sequence { delayed_messages_suffix: u32, #[encoding(dynamic, list)] messages: Vec, - signature: Signature, } /// Message to set the appropriate sequencer @@ -70,7 +96,6 @@ pub struct SetSequencer { nonce: u32, admin_public_key: PublicKey, sequencer_public_key: PublicKey, - signature: Signature, } #[derive(NomReader, BinWriter, Debug, Clone, Eq, PartialEq)] @@ -81,7 +106,7 @@ pub enum SequencerMsg { #[derive(Debug, Clone, PartialEq, Eq)] pub enum KernelMessage { - Sequencer(Framed), + Sequencer(Signed>), DelayedMessage(Vec), } @@ -128,7 +153,7 @@ impl NomReader for KernelMessage { fn nom_read(input: &[u8]) -> NomResult { all_consuming(alt(( all_consuming(map( - preceded(tag([1]), Framed::::nom_read), + preceded(tag([1]), Signed::>::nom_read), KernelMessage::Sequencer, )), map( @@ -155,11 +180,12 @@ impl BinWriter for KernelMessage { #[cfg(test)] mod tests { - use crate::message::{Framed, SequencerMsg}; + use crate::message::{Bytes, Framed, SequencerMsg, Signed}; use super::{KernelMessage, Sequence}; use crate::message::SetSequencer; - use tezos_crypto_rs::hash::{SecretKeyEd25519, SeedEd25519}; + use tezos_crypto_rs::hash::{PublicKeyEd25519, SecretKeyEd25519, SeedEd25519, Signature}; + use tezos_crypto_rs::PublicKeySignatureVerifier; use tezos_data_encoding::enc::{self, BinWriter}; use tezos_data_encoding::nom::NomReader; use tezos_smart_rollup_encoding::public_key::PublicKey; @@ -181,7 +207,7 @@ mod tests { let (_, secret) = key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB"); let signature = secret.sign([0x0]).expect("sign should work"); - let sequence = KernelMessage::Sequencer(Framed { + let body = Framed { destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb") .expect("decoding should work"), payload: SequencerMsg::Sequence(Sequence { @@ -189,9 +215,10 @@ mod tests { delayed_messages_prefix: 0, delayed_messages_suffix: 0, messages: Vec::default(), - signature, }), - }); + }; + + let sequence = KernelMessage::Sequencer(Signed { body, signature }); // Serializing let mut bin: Vec = Vec::new(); @@ -209,16 +236,17 @@ mod tests { key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB"); let signature = secret.sign([0x0]).expect("sign should work"); - let sequence = KernelMessage::Sequencer(Framed { + let body = Framed { destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb") .expect("decoding should work"), payload: SequencerMsg::SetSequencer(SetSequencer { nonce: 0, admin_public_key: public_key.clone(), sequencer_public_key: public_key, - signature, }), - }); + }; + + let sequence = KernelMessage::Sequencer(Signed { body, signature }); // Serializing let mut bin: Vec = Vec::new(); @@ -249,17 +277,18 @@ mod tests { let (_, secret) = key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB"); let signature = secret.sign([0x0]).expect("sign should work"); - let sequence = KernelMessage::Sequencer(Framed { + let body = Framed { destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb") .expect("decoding should work"), payload: SequencerMsg::Sequence(Sequence { nonce: 0, - signature, delayed_messages_prefix: 5, delayed_messages_suffix: 5, messages: Vec::default(), }), - }); + }; + + let sequence = KernelMessage::Sequencer(Signed { body, signature }); // Serializing let mut bin: Vec = Vec::new(); @@ -273,4 +302,250 @@ mod tests { assert!(remaining.is_empty()); assert_eq!(msg_read, KernelMessage::DelayedMessage(bin)) } + + /// Deserialize a string to a KernelMessage + fn from_string(hex: &str) -> KernelMessage { + let hex = hex::decode(hex).expect("valid hexadecimal"); + let (_, msg) = KernelMessage::nom_read(&hex).expect("deserialization should work"); + msg + } + + #[test] + fn test_deserialization_empty_sequence() { + let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + let rollup_address = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + let sign = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + + match kernel_message { + KernelMessage::Sequencer(Signed { + body: + Framed { + destination, + payload: + SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature, + }) => { + assert_eq!(destination, rollup_address); + assert_eq!(nonce, 0); + assert_eq!(delayed_messages_prefix, 0); + assert_eq!(delayed_messages_suffix, 0); + assert_eq!(messages, vec![]); + assert_eq!(signature, sign) + } + _ => panic!("Wrong message encoding"), + } + } + + #[test] + fn test_deserialization_one_empty_message_sequence() { + let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + let rollup_address = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + let sign = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + + println!("{:?}", kernel_message); + + match kernel_message { + KernelMessage::Sequencer(Signed { + body: + Framed { + destination, + payload: + SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature, + }) => { + assert_eq!(destination, rollup_address); + assert_eq!(nonce, 0); + assert_eq!(delayed_messages_prefix, 0); + assert_eq!(delayed_messages_suffix, 0); + assert_eq!(messages, vec![Bytes { inner: vec![] }]); + assert_eq!(signature, sign) + } + _ => panic!("Wrong message encoding"), + } + } + + #[test] + fn test_deserialization_one_message_sequence() { + let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000090000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + let rollup_address = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + let sign = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + + println!("{:?}", kernel_message); + + match kernel_message { + KernelMessage::Sequencer(Signed { + body: + Framed { + destination, + payload: + SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature, + }) => { + assert_eq!(destination, rollup_address); + assert_eq!(nonce, 0); + assert_eq!(delayed_messages_prefix, 0); + assert_eq!(delayed_messages_suffix, 0); + assert_eq!( + messages, + vec![Bytes { + inner: b"hello".to_vec() + }] + ); + assert_eq!(signature, sign) + } + _ => panic!("Wrong message encoding"), + } + } + + #[test] + fn test_deserialization_two_messages_sequence() { + let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000120000000568656c6c6f00000005776f726c6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + let rollup_address = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + let sign = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + + println!("{:?}", kernel_message); + + match kernel_message { + KernelMessage::Sequencer(Signed { + body: + Framed { + destination, + payload: + SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature, + }) => { + assert_eq!(destination, rollup_address); + assert_eq!(nonce, 0); + assert_eq!(delayed_messages_prefix, 0); + assert_eq!(delayed_messages_suffix, 0); + assert_eq!( + messages, + vec![ + Bytes { + inner: b"hello".to_vec() + }, + Bytes { + inner: b"world".to_vec() + } + ] + ); + assert_eq!(signature, sign) + } + _ => panic!("Wrong message encoding"), + } + } + + /// Generate a signed sequence + fn generate_signed_sequence( + secret: SecretKeyEd25519, + destination: SmartRollupAddress, + nonce: u32, + delayed_messages_prefix: u32, + delayed_messages_suffix: u32, + messages: Vec, + ) -> Signed> { + let tmp_sig = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + let mut unsigned = Signed { + body: Framed { + destination, + payload: SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature: tmp_sig, + }; + + let hash = unsigned.hash().unwrap(); + + let signature = secret.sign(hash).unwrap(); + unsigned.signature = signature; + unsigned + } + + #[test] + fn test_incorrect_sequence_signature() { + let destination = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + let nonce = 0; + let delayed_messages_prefix = 0; + let delayed_messages_suffix = 0; + let messages = vec![]; + let signature = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap(); + + let message = Signed { + body: Framed { + destination, + payload: SequencerMsg::Sequence(Sequence { + nonce, + delayed_messages_prefix, + delayed_messages_suffix, + messages, + }), + }, + signature: signature.clone(), + }; + + let hash = message.hash().expect("hash failure"); + + let public_key = PublicKey::Ed25519( + PublicKeyEd25519::from_base58_check( + "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK", + ) + .unwrap(), + ); + + let is_correct = public_key.verify_signature(&signature, &hash); + + matches!(is_correct, Ok(false)); + } + + #[test] + fn test_correct_sequence_signature() { + let destination = + SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap(); + + let seed = SeedEd25519::from_base58_check( + "edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB", + ) + .unwrap(); + + let (public_key, secret_key) = seed.keypair().unwrap(); + + let sequence = generate_signed_sequence(secret_key, destination, 0, 0, 0, vec![]); + + let hash = sequence.hash().unwrap(); + let is_correct = public_key.verify_signature(&sequence.signature, &hash); + matches!(is_correct, Ok(true)); + } } diff --git a/src/lib_scoru_sequencer/dune b/src/lib_scoru_sequencer/dune index 86a4bf7d2decfe7375a313a59ba52c400f43cc3a..1e9814568e24c1e1d10b967c53d1d848fa578362 100644 --- a/src/lib_scoru_sequencer/dune +++ b/src/lib_scoru_sequencer/dune @@ -13,6 +13,8 @@ octez-libs.tezos-workers octez-smart-rollup-node-lib octez-smart-rollup + tezos-client-base + tezos_layer2_store octez-libs.tezos-rpc octez-libs.tezos-rpc-http octez-libs.tezos-rpc-http-server) @@ -24,4 +26,6 @@ -open Tezos_protocol_alpha -open Tezos_workers -open Octez_smart_rollup_node - -open Octez_smart_rollup)) + -open Octez_smart_rollup + -open Tezos_client_base + -open Tezos_layer2_store)) diff --git a/src/lib_scoru_sequencer/kernel_durable.ml b/src/lib_scoru_sequencer/kernel_durable.ml index ac5ed0a68c84a8772f151fbbc23ae927c9fc5abf..5e15a04bf8236906c0ed8d7a5ca1b52ce1b2d7b0 100644 --- a/src/lib_scoru_sequencer/kernel_durable.ml +++ b/src/lib_scoru_sequencer/kernel_durable.ml @@ -23,17 +23,50 @@ (* *) (*****************************************************************************) +open Protocol +open Alpha_context + let sequencer_prefix = "/__sequencer" -let delayed_inbox_prefix = String.concat "/" [sequencer_prefix; "delayed-inbox"] +module Delayed_inbox = struct + let path = String.concat "/" [sequencer_prefix; "delayed-inbox"] + + module Pointer = struct + let path = String.concat "/" [path; "pointer"] + + type t = {head : int32; tail : int32} + + let empty = {head = 0l; tail = -1l} + + let is_adjacent left right = + Compare.Int32.(Int32.succ left.tail = right.head) + + let size x = Int32.(succ @@ sub x.tail x.head) + + let encoding = + let open Data_encoding in + conv (fun {head; tail} -> (head, tail)) (fun (head, tail) -> {head; tail}) + @@ obj2 (req "head" int32) (req "tail" int32) + + let pp ppf p = Format.fprintf ppf "{head: %ld; tail: %ld}" p.head p.tail + end + + module Element = struct + let path element_id = + String.concat "/" [path; "elements"; Int32.to_string element_id] -module Delayed_inbox_pointer = struct - let path = String.concat "/" [delayed_inbox_prefix; "pointer"] + type t = {timeout_level : int32; user_message : string} - type t = {head : int32; tail : int32} + let encoding = + let open Data_encoding in + conv + (fun {timeout_level; user_message} -> (timeout_level, user_message)) + (fun (timeout_level, user_message) -> {timeout_level; user_message}) + @@ obj2 (req "timeout_level" int32) (req "user_message" Variable.string) + end - let encoding = - let open Data_encoding in - conv (fun {head; tail} -> (head, tail)) (fun (head, tail) -> {head; tail}) - @@ obj2 (req "head" int32) (req "tail" int32) + type queue_slice = { + pointer : Pointer.t; + elements : Sc_rollup.Inbox_message.serialized list; + } end diff --git a/src/lib_scoru_sequencer/kernel_message.ml b/src/lib_scoru_sequencer/kernel_message.ml index d7ee9f7f000b48c688ff94d735702d5ed666ea69..5f7685de77d8e82dffc85bee107ea606b89e6229 100644 --- a/src/lib_scoru_sequencer/kernel_message.ml +++ b/src/lib_scoru_sequencer/kernel_message.ml @@ -144,15 +144,25 @@ module Framed_message = Framed (struct && List.equal String.equal x.l2_messages y.l2_messages end) -include Signed (Framed_message) +type unsigned = Framed_message.t -(* Signature consisting of zeroes *) -let dummy_signature = - Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 +let unsigned_encoding = Framed_message.encoding + +module Signed_raw = Signed (struct + type t = string + + let encoding = Data_encoding.Variable.string + + let equal = String.equal +end) + +type signed_raw = Signed_raw.t + +let signed_raw_encoding = Signed_raw.encoding let encode_sequence_message rollup_address ~prefix ~suffix - (l2_messages : Sc_rollup.Inbox_message.serialized list) : string = - (* Fix: actually sign a message *) + (l2_messages : Sc_rollup.Inbox_message.serialized list) : + [`Unsigned_encoded of string] = let unsigned_payload = Framed_message. { @@ -168,18 +178,45 @@ let encode_sequence_message rollup_address ~prefix ~suffix }; } in - let signed_framed_sequence = - {unsigned_payload; signature = dummy_signature} + `Unsigned_encoded + (Data_encoding.Binary.to_string_exn + Framed_message.encoding + unsigned_payload) + +let sign_sequence cctxt signer (`Unsigned_encoded unsigned_sequence) = + let open Lwt_result_syntax in + let+ signature = + Client_keys.V0.sign cctxt signer (String.to_bytes unsigned_sequence) + in + Data_encoding.Binary.to_string_exn + signed_raw_encoding + {unsigned_payload = unsigned_sequence; signature} + +let encode_and_sign_sequence (cctx, signer) rollup_address ~prefix ~suffix + serialized_msgs = + let encoded_sequence = + encode_sequence_message rollup_address ~prefix ~suffix serialized_msgs in - Data_encoding.Binary.to_string_exn encoding signed_framed_sequence + sign_sequence cctx signer encoded_sequence let single_l2_message_overhead = let dummy_address = Sc_rollup.Address.of_b58check_exn "sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb" in - String.length - @@ encode_sequence_message - dummy_address - ~prefix:1000l - ~suffix:1000l - [Sc_rollup.Inbox_message.unsafe_of_string ""] + (* Signature consisting of zeroes *) + let dummy_signature = + Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 + in + let (`Unsigned_encoded encoded) = + encode_sequence_message + dummy_address + ~prefix:1000l + ~suffix:1000l + [Sc_rollup.Inbox_message.unsafe_of_string ""] + in + let dummy_signed = + Data_encoding.Binary.to_string_exn + signed_raw_encoding + {unsigned_payload = encoded; signature = dummy_signature} + in + String.length dummy_signed diff --git a/src/lib_scoru_sequencer/optimistic_simulation.ml b/src/lib_scoru_sequencer/optimistic_simulation.ml new file mode 100644 index 0000000000000000000000000000000000000000..df53250061b71591c35fab230b29e41493047094 --- /dev/null +++ b/src/lib_scoru_sequencer/optimistic_simulation.ml @@ -0,0 +1,299 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022-2023 TriliTech *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Protocol +open Alpha_context +open Octez_smart_rollup_node_alpha +open Kernel_durable +module Fueled_pvm = Fueled_pvm.Free + +type t = { + current_block_diff : Delayed_inbox.Pointer.t; + inbox_level : int32; + ctxt : Context.ro; + state : Context.tree; + tot_messages_consumed : int; + accumulated_messages : Sc_rollup.Inbox_message.serialized list; + block_beginning : Context.tree; +} + +module type Messages_encoder = sig + type signer_ctxt + + val encode_sequence : + signer_ctxt -> + nonce:int32 -> + prefix:int32 -> + suffix:int32 -> + Sc_rollup.Inbox_message.serialized list -> + string tzresult Lwt.t +end + +module type S = sig + type signer_ctxt + + val init_ctxt : + signer_ctxt -> + Node_context.ro -> + Delayed_inbox.queue_slice -> + t tzresult Lwt.t + + val new_block : + signer_ctxt -> + Node_context.ro -> + t -> + Delayed_inbox.queue_slice -> + t tzresult Lwt.t + + val append_messages : + signer_ctxt -> + Node_context.ro -> + t -> + Sc_rollup.Inbox_message.serialized list -> + t tzresult Lwt.t +end + +module Simple = struct + include Internal_event.Simple + + let section = ["sequencer_node"] + + let simulation_kernel_debug = + declare_1 + ~section + ~name:"simulation_kernel_debug" + ~level:Info + ~msg:"Simulation debug: {log}" + ("log", Data_encoding.string) + ~pp1:Format.pp_print_string +end + +let simulation_kernel_debug msg = Simple.(emit simulation_kernel_debug) msg + +let init_empty_ctxt node_ctxt (first_block : Delayed_inbox.Pointer.t) = + let open Lwt_result_syntax in + let genesis_level = node_ctxt.Node_context.genesis_info.level in + let* genesis_hash = Node_context.hash_of_level node_ctxt genesis_level in + let genesis_head = Layer1.{hash = genesis_hash; level = genesis_level} in + let* ctxt = Node_context.checkout_context node_ctxt genesis_hash in + let+ ctxt, state = Interpreter.state_of_head node_ctxt ctxt genesis_head in + { + ctxt; + (* It will be incremeneted in new_block_impl straight away *) + inbox_level = node_ctxt.Node_context.genesis_info.level; + state; + current_block_diff = first_block; + tot_messages_consumed = 0; + accumulated_messages = []; + block_beginning = state; + } + +let simulate (node_ctxt : Node_context.ro) + ({ctxt; inbox_level; accumulated_messages; block_beginning; _} as sim) + messages = + let open Lwt_result_syntax in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! block_beginning_hash = PVM.state_hash block_beginning in + let*! tick = PVM.get_tick block_beginning in + let accumulated_messages = List.rev_append messages accumulated_messages in + let*? eol = + Sc_rollup.Inbox_message.serialize + (Sc_rollup.Inbox_message.Internal End_of_level) + |> Environment.wrap_tzresult + in + let eval_state = + Fueled_pvm. + { + state = block_beginning; + state_hash = block_beginning_hash; + tick; + inbox_level; + message_counter_offset = 0; + remaining_fuel = Fuel.Free.of_ticks 0L; + remaining_messages = + List.map Sc_rollup.Inbox_message.unsafe_to_string + @@ List.rev (eol :: accumulated_messages); + } + in + let node_ctxt = + Node_context.{node_ctxt with kernel_debug_logger = simulation_kernel_debug} + in + let* eval_result = Fueled_pvm.eval_messages node_ctxt eval_state in + (* Build new state *) + let Fueled_pvm.{state = {state; _}; _} = + Delayed_write_monad.ignore eval_result + in + let*! ctxt = PVM.State.set ctxt state in + return {sim with ctxt; state; accumulated_messages} + +let ensure_diff_is_valid (current_diff : Delayed_inbox.Pointer.t option) + (new_diff : Delayed_inbox.queue_slice) = + let open Lwt_result_syntax in + let* () = + match current_diff with + | None -> + fail_unless + Compare.Int32.(new_diff.pointer.head = 0l) + (Exn + (Failure + "First block's messages have to be the first in the delayed \ + inbox queue")) + | Some current_diff -> + fail_unless + (Delayed_inbox.Pointer.is_adjacent current_diff new_diff.pointer) + (Exn + (Failure + "Two consecutive blocks' message have to be adjacent in the \ + delayed inbox queue")) + in + let open Sc_rollup.Inbox_message in + let els = new_diff.elements in + match (els, List.last_opt els) with + | sol :: ipl :: _, Some eol -> ( + let*? sol_ = Environment.wrap_tzresult @@ deserialize sol in + let*? ipl_ = Environment.wrap_tzresult @@ deserialize ipl in + let*? eol_ = Environment.wrap_tzresult @@ deserialize eol in + match (sol_, ipl_, eol_) with + | ( Internal Start_of_level, + Internal (Info_per_level _), + Internal End_of_level ) -> + return (List.take_n (List.length els - 1) els, eol) + | _ -> + tzfail + (Exn + (Failure + "Boundaries messages are expected to be SoL, IpL, .. EoL"))) + | _ -> + tzfail + (Exn + (Failure + "Block diff has to include at least SoL, IpL and EoL messages")) + +module Make (Enc : Messages_encoder) = struct + (* First supply block diff to the sequencer kernel but the last EoL, + all of them will land in the delayed inbox queue. + Then suppply Sequence that consumes all those messages from the delayed inbox. *) + let new_block_impl ?current_block_diff signer_ctxt node_ctxt sim_ctxt + new_block_diff = + let open Lwt_result_syntax in + let* all_but_eol, _eol = + ensure_diff_is_valid current_block_diff new_block_diff + in + (* If we already started a first block, we have to close it up with EoL, + which is supposed to be on the head of the queue. *) + let is_first_block = Option.is_none current_block_diff in + (* First, move sim_ctxt to state where EoL of the current block is supplied. + To do so we just need to update block beginning state to state as it's basically needed one, + reset accumulated messages and update current_block_diff *) + let sim_ctxt = + { + sim_ctxt with + accumulated_messages = []; + block_beginning = sim_ctxt.state; + inbox_level = Int32.succ sim_ctxt.inbox_level; + current_block_diff = new_block_diff.pointer; + } + in + let* populated_delayed_inbox_ctxt = + simulate node_ctxt sim_ctxt all_but_eol + in + let diff_size = + Kernel_durable.Delayed_inbox.Pointer.size new_block_diff.pointer + in + let to_consume = + if is_first_block then + (* In this case, it's our first block, hence, no EoL from previous level *) + Int32.pred diff_size + else + (* In this case delayed inbox looks like: + EoL[from previous level], SoL[new level], IpL[new level], ..., . + We need to consume first EoL and the rest of the messages, + what brings us to diff_size elements to consume. + *) diff_size + in + (* TODO: fetch optimistic nonce *) + let* consume_inbox_sequence = + Enc.encode_sequence signer_ctxt ~nonce:0l ~prefix:to_consume ~suffix:0l [] + in + let*? wrapping_sequence_external = + Environment.wrap_tzresult + @@ Sc_rollup.Inbox_message.(serialize @@ External consume_inbox_sequence) + in + let* consumed_delayed_inbox_ctxt = + simulate + node_ctxt + populated_delayed_inbox_ctxt + [wrapping_sequence_external] + in + return + { + consumed_delayed_inbox_ctxt with + tot_messages_consumed = + consumed_delayed_inbox_ctxt.tot_messages_consumed + + Int32.to_int to_consume; + } + + let init_ctxt signer_ctxt node_ctxt (first_block : Delayed_inbox.queue_slice) + = + let open Lwt_result_syntax in + let* init_ctxt = init_empty_ctxt node_ctxt first_block.pointer in + new_block_impl signer_ctxt node_ctxt init_ctxt first_block + + let new_block signer_ctxt node_ctxt sim_state block_delayed_inbox_diff = + new_block_impl + ~current_block_diff:sim_state.current_block_diff + signer_ctxt + node_ctxt + sim_state + block_delayed_inbox_diff + + (* Construct Sequence consisting of the passed blocks and passes it to the sequencer kernel *) + let append_messages signer_ctxt node_ctxt sim_state + external_serialized_messages = + let open Lwt_result_syntax in + (* TODO fetch nonce from simulation durable storage *) + let* encoded_wrapping_sequence = + Enc.encode_sequence + signer_ctxt + ~nonce:0l + ~prefix:0l + ~suffix:0l + external_serialized_messages + in + let*? wrapping_sequence_external = + Environment.wrap_tzresult + @@ Sc_rollup.Inbox_message.( + serialize @@ External encoded_wrapping_sequence) + in + let+ consumed_ctxt = + simulate node_ctxt sim_state [wrapping_sequence_external] + in + { + consumed_ctxt with + tot_messages_consumed = + consumed_ctxt.tot_messages_consumed + + List.length external_serialized_messages; + } +end diff --git a/src/lib_scoru_sequencer/optimistic_simulation.mli b/src/lib_scoru_sequencer/optimistic_simulation.mli new file mode 100644 index 0000000000000000000000000000000000000000..169cc72fc394e20cdb18246ca7d0756700c0f956 --- /dev/null +++ b/src/lib_scoru_sequencer/optimistic_simulation.mli @@ -0,0 +1,106 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022-2023 TriliTech *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Protocol +open Alpha_context +open Kernel_durable + +(** This module simulates sequence of the messages + in which they will be eventually supplied to the sequencer kernel. + It's doing so by replicating the exact same order + defined in the seq_batcher, basically reacting to the two types of events: + - new finalized block added to the simulation + - user messages injected through sequencer RPC added to the simulation + + Currently, the simulation works in a quadratic way, + resimulating all the sequence of the messages when new messages arrives. + It will be fixed when this https://gitlab.com/tezos/tezos/-/issues/6020 task is done. +*) + +(** Provides capability to encode well formed sequence, + which will be supplied to the simulation PVM *) +module type Messages_encoder = sig + type signer_ctxt + + val encode_sequence : + signer_ctxt -> + nonce:int32 -> + prefix:int32 -> + suffix:int32 -> + Sc_rollup.Inbox_message.serialized list -> + string tzresult Lwt.t +end + +type t = { + current_block_diff : Delayed_inbox.Pointer.t; + (** The range of the delayed inbox queue, + which have been added to the delayed inbox by the current block. + Head of this pointer corresponds to the head of the queue. + *) + inbox_level : int32; + (** Current inbox level, incremented when a new block arrives *) + ctxt : Context.ro; + state : Context.tree; + (** State of the PVM corresponding to all [accumulated_messages] @@ EoL *) + tot_messages_consumed : int; + (** How many messages have been fed to the user kernel by the sequencer kernel *) + accumulated_messages : Sc_rollup.Inbox_message.serialized list; + (** All the messages, which have to be fed to the sequencer kernel since the last block. + Workaround causing quadratic complexity of the simulation, will be removed when + https://gitlab.com/tezos/tezos/-/issues/6020 is done *) + block_beginning : Context.tree; + (** State of the PVM corresponding to the previous block. + Will be removed when https://gitlab.com/tezos/tezos/-/issues/6020 is done *) +} + +module type S = sig + type signer_ctxt + + (** Init simulation context with the first block, having level genesis + 1. *) + val init_ctxt : + signer_ctxt -> + Node_context.ro -> + Delayed_inbox.queue_slice -> + t tzresult Lwt.t + + (** New block arrives, adding a difference to the delayed inbox. *) + val new_block : + signer_ctxt -> + Node_context.ro -> + t -> + Delayed_inbox.queue_slice -> + t tzresult Lwt.t + + (** Append more messages to the simulation. *) + val append_messages : + signer_ctxt -> + Node_context.ro -> + t -> + Sc_rollup.Inbox_message.serialized list -> + t tzresult Lwt.t +end + +module Make (Enc : Messages_encoder) : + S with type signer_ctxt := Enc.signer_ctxt diff --git a/src/lib_scoru_sequencer/seq_batcher.ml b/src/lib_scoru_sequencer/seq_batcher.ml index 54a98a3cc7f491222b58d8885fa99e79cc4d7e26..bd9c22bbabfb63ad3395ccd91059b1e6743c6307 100644 --- a/src/lib_scoru_sequencer/seq_batcher.ml +++ b/src/lib_scoru_sequencer/seq_batcher.ml @@ -45,7 +45,8 @@ module Batched_messages = Hash_queue.Make (L2_message.Hash) (L2_batched_message) type state = { node_ctxt : Node_context.ro; - signer : Signature.public_key_hash; + (* This one is V0 because sequencer kernel doesn't support BLS signatures yet *) + signer : Signature.V0.public_key_hash * Client_keys.sk_uri; messages : Message_queue.t; batched : Batched_messages.t; mutable simulation_ctxt : Simulation.t option; @@ -56,7 +57,9 @@ let inject_sequence state (sequencer_message, l2_messages) = let open Lwt_result_syntax in let operation = L1_operation.Add_messages {messages = [sequencer_message]} in let+ l1_hash = - Injector.add_pending_operation ~source:state.signer operation + Injector.add_pending_operation + ~source:(Signature.Of_V0.public_key_hash @@ fst state.signer) + operation in List.iter (fun msg -> @@ -75,9 +78,8 @@ let get_previous_delayed_inbox_size node_ctxt (head : Layer1.head) = (Exn (Failure "Cannot obtain delayed inbox before origination level")) in let* previous_head = Node_context.get_predecessor node_ctxt head in - let first_inbox_level = Int32.succ node_ctxt.genesis_info.level in let* ctxt = - if previous_head.level < first_inbox_level then + if previous_head.level < node_ctxt.genesis_info.level then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) @@ -94,15 +96,15 @@ let get_previous_delayed_inbox_size node_ctxt (head : Layer1.head) = previous_head in let open Kernel_durable in - let*! pointer_bytes = Durable_state.lookup state Delayed_inbox_pointer.path in + let*! pointer_bytes = Durable_state.lookup state Delayed_inbox.Pointer.path in match pointer_bytes with | None -> return 0 | Some pointer_bytes -> return @@ Option.fold ~none:0 ~some:(fun x -> - Int32.(to_int @@ sub x.Delayed_inbox_pointer.tail x.head)) + Int32.(to_int @@ sub x.Delayed_inbox.Pointer.tail x.head)) @@ Data_encoding.Binary.of_bytes_opt - Delayed_inbox_pointer.encoding + Delayed_inbox.Pointer.encoding pointer_bytes let get_batch_sequences state head = @@ -125,16 +127,15 @@ let get_batch_sequences state head = (* That might happen only for the genesis + 1 block level *) return ([], 0) else - return - ( [ - ( Kernel_message.encode_sequence_message - state.node_ctxt.rollup_address - ~prefix:(Int32.of_int (delayed_inbox_size - 1)) - ~suffix:1l - l2_messages_serialized, - l2_messages ); - ], - delayed_inbox_size ) + let* signed_sequence = + Kernel_message.encode_and_sign_sequence + (state.node_ctxt.cctxt, snd state.signer) + state.node_ctxt.rollup_address + ~prefix:(Int32.of_int (delayed_inbox_size - 1)) + ~suffix:1l + l2_messages_serialized + in + return ([(signed_sequence, l2_messages)], delayed_inbox_size) let produce_batch_sequences state head = let open Lwt_result_syntax in @@ -225,10 +226,14 @@ let on_new_head state head = return_unit let init_batcher_state node_ctxt ~signer = - Lwt.return + let open Lwt_result_syntax in + let* _alias, _pk, sk = + Client_keys.V0.get_key node_ctxt.Node_context.cctxt signer + in + return { node_ctxt; - signer; + signer = (signer, sk); messages = Message_queue.create 100_000 (* ~ 400MB *); batched = Batched_messages.create 100_000 (* ~ 400MB *); simulation_ctxt = None; @@ -278,7 +283,20 @@ module Handlers = struct let on_launch _w () Types.{node_ctxt; signer} = let open Lwt_result_syntax in - let*! state = init_batcher_state node_ctxt ~signer in + let to_v0_exn pkh = + match + Signature.V0.Public_key_hash.of_bytes + @@ Signature.Public_key_hash.to_bytes pkh + with + | Error e -> + failwith + "Couldn't convert an operator key to V0 key: %a" + Error_monad.pp_print_trace + e + | Ok x -> return x + in + let* signer = to_v0_exn signer in + let* state = init_batcher_state node_ctxt ~signer in return state let on_error (type a b) _w st (r : (a, b) Request.t) (errs : b) : diff --git a/src/lib_scoru_sequencer/test/test_kernel_message.ml b/src/lib_scoru_sequencer/test/test_kernel_message.ml index 1d733a8818fa15d8e2271e2821eae3bceaeeb929..60de0c11a61400c9f2a1644882d4e66660ce7d59 100644 --- a/src/lib_scoru_sequencer/test/test_kernel_message.ml +++ b/src/lib_scoru_sequencer/test/test_kernel_message.ml @@ -35,9 +35,18 @@ open Alpha_context open Tztest module KM = Octez_smart_rollup_sequencer.Kernel_message -let test_signed ?loc expected_hex encoded_msg = +(* TODO fix it: actually sign it *) +let test_encoding ?loc expected_hex (`Unsigned_encoded encoded_msg) = let open Lwt_result_syntax in - let result_hex = Hex.show @@ Hex.of_string encoded_msg in + let dummy_signature = + Signature.V0.of_bytes_exn @@ Bytes.make Signature.V0.size @@ Char.chr 0 + in + let dummy_signed = + Data_encoding.Binary.to_string_exn + KM.signed_raw_encoding + {unsigned_payload = encoded_msg; signature = dummy_signature} + in + let result_hex = Hex.show @@ Hex.of_string dummy_signed in Assert.equal ?loc ~msg:"Encoded hex don't match" expected_hex result_hex ; return_unit @@ -52,7 +61,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ empty_l2_messages_hex (KM.encode_sequence_message rollup_address ~prefix:0l ~suffix:0l []) @@ -62,7 +71,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ single_empty_l2_message_hex (KM.encode_sequence_message @@ -76,7 +85,7 @@ let test_empty_suffix_n_prefix () = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000090000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in let* () = - test_signed + test_encoding ~loc:__LOC__ hello_l2_message_hex (KM.encode_sequence_message @@ -89,7 +98,7 @@ let test_empty_suffix_n_prefix () = let hello_world_l2_message_hex = "006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000120000000568656c6c6f00000005776f726c6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" in - test_signed + test_encoding ~loc:__LOC__ hello_world_l2_message_hex (KM.encode_sequence_message diff --git a/tezt/tests/sc_sequencer.ml b/tezt/tests/sc_sequencer.ml index e0aaab0f2b4ec6355a780fb5638ac2b3ed61a922..9c2e9e70b7f0c4e4951a1c966bcba326f623e644 100644 --- a/tezt/tests/sc_sequencer.ml +++ b/tezt/tests/sc_sequencer.ml @@ -29,7 +29,6 @@ Component: Smart Optimistic Rollups: Sequencer Invocation: dune exec tezt/tests/main.exe -- --file sc_sequencer.ml *) - open Sc_rollup_helpers open Tezos_protocol_alpha.Protocol @@ -55,39 +54,59 @@ let next_rollup_level {node; client; sc_sequencer_node; _} = let setup_sequencer_kernel ?(originator_key = Constant.bootstrap1.public_key_hash) ?(sequencer_key = Constant.bootstrap1.public_key_hash) protocol = - let* node, client = setup_l1 protocol in + (* Prepare sequencer kernel & originate it *) + let data_dir = Temp.dir "sequencer-kernel-data-dir" in + let* boot_sector = + prepare_installer_kernel + ~base_installee:"./" + ~config: + [ + Installer_kernel_config.Set + { + value = + (* encodings of State::Sequenced(edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav) *) + "00004798d2cc98473d7e250c898885718afd2e4efbcb1a1595ab9730761ed830de0f"; + to_ = "/__sequencer/state"; + }; + ] + ~preimages_dir:(Filename.concat data_dir "wasm_2_0_0") + "sequenced_kernel" + in + let boot_sector_file = Filename.temp_file "boot-sector" ".hex" in + let () = write_file boot_sector_file ~contents:boot_sector in + let* parameters_ty = + let client = Client.create_with_mode Client.Mockup in + Client.convert_data_to_json ~data:"unit" client + in + let sc_rollup_address = "sr1ExAn7W4MzgEoAWqhHwWivGCt3W8r1qH7g" in + let bootstrap_tx_kernel : Protocol.bootstrap_smart_rollup = + { + address = sc_rollup_address; + pvm_kind = "wasm_2_0_0"; + boot_sector; + parameters_ty; + } + in + + (* Run Tezos node & Sequencer node*) + let* node, client = + setup_l1 ~bootstrap_smart_rollups:[bootstrap_tx_kernel] protocol + in let sc_sequencer_node = Sc_rollup_node.create Custom node ~path:"./octez-smart-rollup-sequencer-node" + ~data_dir ~base_dir:(Client.base_dir client) ~default_operator:sequencer_key in - (* Prepare sequencer kernel & originate it *) - let* boot_sector = - prepare_installer_kernel - ~base_installee:"./" - ~preimages_dir: - (Filename.concat - (Sc_rollup_node.data_dir sc_sequencer_node) - "wasm_2_0_0") - "sequenced_kernel" - in - let* sc_rollup_address = - originate_sc_rollup - ~kind:pvm_kind - ~boot_sector - ~parameters_ty:"unit" - ~src:originator_key - client - in - (* Start a sequencer node *) + let* () = Client.bake_for_and_wait client in let* () = Sc_rollup_node.run_sequencer sc_sequencer_node sc_rollup_address - ["--log-kernel-debug"] + ["--log-kernel-debug"; "--boot-sector-file"; boot_sector_file] in let sc_rollup_client = Sc_rollup_client.create ~protocol sc_sequencer_node in return @@ -123,8 +142,7 @@ let send_message ~src client raw_msg = let wait_for_sequence_debug_message sc_node = Sc_rollup_node.wait_for sc_node "kernel_debug.v0" @@ fun json -> let message = JSON.as_string json in - if String.starts_with ~prefix:"Received a sequence message" message then - Some message + if String.starts_with ~prefix:"Received Sequence {" message then Some message else None let test_delayed_inbox_consumed = @@ -201,22 +219,14 @@ let test_delayed_inbox_consumed = let expected_sequences = List.map (fun (delayed_inbox_prefix, delayed_inbox_suffix) -> Format.sprintf - "Received a sequence message Sequence { nonce: 0, \ - delayed_messages_prefix: %d, delayed_messages_suffix: %d, messages: \ - [], signature: \ - Signature(\"edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q\") \ - } targeting our rollup" + "Received Sequence { nonce: 0, delayed_messages_prefix: %d, \ + delayed_messages_suffix: %d, messages: [] } targeting our rollup" delayed_inbox_prefix delayed_inbox_suffix) @@ [(4, 1); (7, 1)] in - Check.( - ( = ) - expected_sequences - (List.rev !collected_sequences) - ~__LOC__ - (list string) - ~error_msg:"Unexpected debug messages emitted") ; + Check.((expected_sequences = List.rev !collected_sequences) (list string)) + ~error_msg:"Unexpected debug messages emitted:, should be %L, but got %R" ; Lwt.return_unit let register ~protocols = test_delayed_inbox_consumed protocols