From 59b2a473c9b3707b5673495eec99c64ede981de3 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 10 Apr 2025 17:54:46 +0200 Subject: [PATCH 1/9] Tezlink/Kernel: Dont know why but my rust analyzer don't like from str anymore --- etherlink/kernel_latest/tezos/src/block.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs index 2390ae6bbc1c..efc84ddbbf5b 100644 --- a/etherlink/kernel_latest/tezos/src/block.rs +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use primitive_types::{H256, U256}; -use std::{array::TryFromSliceError, str::FromStr}; +use std::array::TryFromSliceError; use tezos_crypto_rs::blake2b::digest_256; use tezos_smart_rollup::types::Timestamp; @@ -20,8 +20,12 @@ impl TezBlock { pub fn genesis_block_hash() -> H256 { // This H256 comes from this b58 hash 'BLockGenesisGenesisGenesisGenesisGenesis1db77eJNeJ9' // That is the ghostnet genesis hash according to 'devtools/get_contracts/config.ml' - H256::from_str("8fcf233671b6a04fcf679d2a381c2544ea6c1ea29ba6157776ed8423e7c02934") - .unwrap() + H256::from_slice( + &hex::decode( + "8fcf233671b6a04fcf679d2a381c2544ea6c1ea29ba6157776ed8423e7c02934", + ) + .unwrap(), + ) } fn hash(&self) -> H256 { -- GitLab From b1e889a97894be75310de33ab167303d923c4744 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 17 Apr 2025 15:16:35 +0200 Subject: [PATCH 2/9] Tezlink/Kernel/Node: Implement NomRead and Binwrite for block Also modify the decoding of a Tezlink block in the node by reintroducing the hash in the block. This makes things easier in the node and there was no real reason to not have it. --- .../bin_node/lib_dev/encodings/l2_types.ml | 45 ++---- .../bin_node/lib_dev/encodings/l2_types.mli | 2 +- .../kernel_latest/kernel/src/block_storage.rs | 2 +- etherlink/kernel_latest/kernel/src/chains.rs | 2 +- etherlink/kernel_latest/kernel/src/l2block.rs | 6 +- etherlink/kernel_latest/tezos/src/block.rs | 129 ++++++++++++------ 6 files changed, 107 insertions(+), 79 deletions(-) diff --git a/etherlink/bin_node/lib_dev/encodings/l2_types.ml b/etherlink/bin_node/lib_dev/encodings/l2_types.ml index 7b8f3acf365a..dc04842d7636 100644 --- a/etherlink/bin_node/lib_dev/encodings/l2_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/l2_types.ml @@ -48,15 +48,9 @@ module Chain_family = struct end module Tezos_block = struct - type block_without_hash = { - level : int32; - timestamp : Time.Protocol.t; - parent_hash : Ethereum_types.block_hash; - } - type t = { - level : int32; hash : Ethereum_types.block_hash; + level : int32; timestamp : Time.Protocol.t; parent_hash : Ethereum_types.block_hash; } @@ -69,7 +63,7 @@ module Tezos_block = struct Ethereum_types.Block_hash (Hex "8fcf233671b6a04fcf679d2a381c2544ea6c1ea29ba6157776ed8423e7c02934") - let block_without_hash_encoding : block_without_hash Data_encoding.t = + let block_encoding : t Data_encoding.t = let open Data_encoding in let timestamp_encoding = Time.Protocol.encoding in let block_hash_encoding = @@ -83,36 +77,25 @@ module Tezos_block = struct in def "tezlink_block" @@ conv - (fun ({level; parent_hash; timestamp} : block_without_hash) -> - (level, parent_hash, timestamp)) - (fun (level, parent_hash, timestamp) -> - {level; parent_hash; timestamp}) - (obj3 + (fun {hash; level; parent_hash; timestamp} -> + (hash, level, parent_hash, timestamp)) + (fun (hash, level, parent_hash, timestamp) -> + {hash; level; parent_hash; timestamp}) + (obj4 + (req "hash" block_hash_encoding) (req "level" int32) (req "parent_hash" block_hash_encoding) (req "timestamp" timestamp_encoding)) - let () = Data_encoding.Registration.register block_without_hash_encoding + let () = Data_encoding.Registration.register block_encoding (* This function may be replaced in the future by an already existing function *) (* When Tezos block will be complete *) - let block_from_binary bytes : t = - let ({level; parent_hash; timestamp} : block_without_hash) = - Data_encoding.Binary.of_bytes_exn block_without_hash_encoding bytes - in - let block_hash = Block_hash.hash_bytes [bytes] in - let hash = - Ethereum_types.decode_block_hash (Block_hash.to_bytes block_hash) - in - {level; parent_hash; timestamp; hash} - - let encode_block ({level; parent_hash; timestamp; hash = _} : t) : - (string, string) result = - Ok - (Bytes.to_string - @@ Data_encoding.Binary.to_bytes_exn - block_without_hash_encoding - {level; parent_hash; timestamp}) + let block_from_binary bytes = + Data_encoding.Binary.of_bytes_exn block_encoding bytes + + let encode_block (block : t) : (string, string) result = + Ok (Data_encoding.Binary.to_string_exn block_encoding block) let decode_block (b : string) : (t, string) result = let b = Bytes.of_string b in diff --git a/etherlink/bin_node/lib_dev/encodings/l2_types.mli b/etherlink/bin_node/lib_dev/encodings/l2_types.mli index 3173eb34fac0..865f7fdc6ac7 100644 --- a/etherlink/bin_node/lib_dev/encodings/l2_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/l2_types.mli @@ -46,8 +46,8 @@ end module Tezos_block : sig type t = { - level : int32; hash : Ethereum_types.block_hash; + level : int32; timestamp : Time.Protocol.t; parent_hash : Ethereum_types.block_hash; } diff --git a/etherlink/kernel_latest/kernel/src/block_storage.rs b/etherlink/kernel_latest/kernel/src/block_storage.rs index 1544192cb693..e6b4171c7925 100644 --- a/etherlink/kernel_latest/kernel/src/block_storage.rs +++ b/etherlink/kernel_latest/kernel/src/block_storage.rs @@ -92,7 +92,7 @@ fn store_block( index.push_value(host, block.hash().as_bytes())?; } let path = path::path(root, block.hash())?; - let bytes = block.to_bytes(); + let bytes = block.to_bytes()?; Ok(host.store_write_all(&path, &bytes)?) } diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index b143e55d0011..99052b26729e 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -455,7 +455,7 @@ impl ChainConfigTrait for MichelsonChainConfig { number ); - let tezblock = TezBlock::new(number, timestamp, previous_hash); + let tezblock = TezBlock::new(number, timestamp, previous_hash)?; let new_block = L2Block::Tezlink(tezblock); let root = self.storage_root_path(); crate::block_storage::store_current(host, &root, &new_block) diff --git a/etherlink/kernel_latest/kernel/src/l2block.rs b/etherlink/kernel_latest/kernel/src/l2block.rs index c5dd2011726a..b49afba7f75c 100644 --- a/etherlink/kernel_latest/kernel/src/l2block.rs +++ b/etherlink/kernel_latest/kernel/src/l2block.rs @@ -62,10 +62,10 @@ impl L2Block { } } - pub fn to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> anyhow::Result> { match self { - Self::Etherlink(block) => block.to_bytes(), - Self::Tezlink(block) => block.to_bytes(), + Self::Etherlink(block) => Ok(block.to_bytes()), + Self::Tezlink(block) => Ok(block.to_bytes()?), } } diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs index efc84ddbbf5b..7bc12b7a93ac 100644 --- a/etherlink/kernel_latest/tezos/src/block.rs +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -2,20 +2,75 @@ // // SPDX-License-Identifier: MIT +use nom::bytes::complete::take; +use nom::combinator::map; +use nom::error::ParseError; +use nom::Finish; use primitive_types::{H256, U256}; -use std::array::TryFromSliceError; use tezos_crypto_rs::blake2b::digest_256; +use tezos_data_encoding::enc as tezos_enc; +use tezos_data_encoding::nom as tezos_nom; +use tezos_data_encoding::nom::error::DecodeError; +use tezos_data_encoding::nom::NomError; +use tezos_enc::{BinError, BinWriter}; +use tezos_nom::{NomReader, NomResult}; use tezos_smart_rollup::types::Timestamp; // WIP: This structure will evolve to look like Tezos block #[derive(PartialEq, Debug)] pub struct TezBlock { - pub number: U256, pub hash: H256, + pub number: U256, pub timestamp: Timestamp, pub previous_hash: H256, } +impl NomReader<'_> for TezBlock { + fn nom_read(input: &'_ [u8]) -> NomResult<'_, Self> { + let (remaining, hash) = + map(take::(32_usize), H256::from_slice)(input)?; + + let (remaining, number) = nom::number::complete::be_u32(remaining)?; + let number = U256::from(number); + + let (remaining, previous_hash) = + map(take::(32_usize), H256::from_slice)(remaining)?; + + // Decode the timestamp + let (remaining, timestamp) = nom::number::complete::be_i64(remaining)?; + let timestamp = Timestamp::from(timestamp); + + Ok(( + remaining, + Self { + hash, + number, + timestamp, + previous_hash, + }, + )) + } +} + +impl BinWriter for TezBlock { + // Encoded size for parameter were taken from this command: + // `octez-codec describe block_header binary schema` + fn bin_write(&self, output: &mut Vec) -> Result<(), BinError> { + let Self { + hash, + number, + timestamp, + previous_hash, + } = self; + // Encode all block fields + tezos_enc::put_bytes(&hash.to_fixed_bytes(), output); + tezos_enc::u32(&number.as_u32(), output)?; + tezos_enc::put_bytes(&previous_hash.to_fixed_bytes(), output); + tezos_enc::i64(×tamp.i64(), output)?; + Ok(()) + } +} + impl TezBlock { pub fn genesis_block_hash() -> H256 { // This H256 comes from this b58 hash 'BLockGenesisGenesisGenesisGenesisGenesis1db77eJNeJ9' @@ -28,59 +83,46 @@ impl TezBlock { ) } - fn hash(&self) -> H256 { - let encoded_data = self.to_bytes(); + // This function must be used on a TezBlock whose hash field is H256::zero() + fn hash(&self) -> Result { + let mut encoded_data = vec![]; + self.bin_write(&mut encoded_data)?; let hashed_data = digest_256(&encoded_data); - H256::from_slice(&hashed_data) + Ok(H256::from_slice(&hashed_data)) } - pub fn new(number: U256, timestamp: Timestamp, previous_hash: H256) -> Self { + pub fn new( + number: U256, + timestamp: Timestamp, + previous_hash: H256, + ) -> Result { let block = Self { hash: H256::zero(), number, timestamp, previous_hash, }; - Self { - hash: block.hash(), + Ok(Self { + hash: block.hash()?, ..block - } + }) } - // Encoded size for parameter were taken from this command: - // `octez-codec describe block_header binary schema` - pub fn to_bytes(&self) -> Vec { - let Self { - hash: _, - number, - timestamp, - previous_hash, - } = self; - let mut data = vec![]; - - // Encode all block fields - let num_enc: [u8; 4] = number.as_u32().to_be_bytes(); - let predecessor: [u8; 32] = previous_hash.to_fixed_bytes(); - let time_enc: [u8; 8] = timestamp.i64().to_be_bytes(); - - // Append encoded fields to data - data.extend_from_slice(&num_enc); - data.extend_from_slice(&predecessor); - data.extend_from_slice(&time_enc); - - data + pub fn to_bytes(&self) -> Result, BinError> { + let mut output = vec![]; + self.bin_write(&mut output)?; + Ok(output) } - pub fn try_from_bytes(bytes: &[u8]) -> Result { - let number = U256::from_big_endian(&bytes[0..4]); - - let previous_hash = H256::from_slice(&bytes[4..36]); - - // Decode the timestamp - let timestamp_array: [u8; 8] = bytes[36..44].try_into()?; - let timestamp = Timestamp::from(i64::from_be_bytes(timestamp_array)); - - Ok(TezBlock::new(number, timestamp, previous_hash)) + pub fn try_from_bytes(bytes: &[u8]) -> Result> { + let (remaining, block) = Self::nom_read(bytes).finish()?; + if !remaining.is_empty() { + return Err(DecodeError::from_error_kind( + remaining, + nom::error::ErrorKind::NonEmpty, + )); + } + Ok(block) } } @@ -92,7 +134,9 @@ mod tests { use super::TezBlock; pub fn block_roundtrip(block: TezBlock) { - let bytes = block.to_bytes(); + let bytes = block + .to_bytes() + .expect("Block encoding should have succeeded"); let decoded_block = TezBlock::try_from_bytes(&bytes).expect("Block should be decodable"); assert_eq!(block, decoded_block, "Roundtrip failed on {:?}", block) @@ -103,6 +147,7 @@ mod tests { let timestamp = Timestamp::from(0); let previous_hash = TezBlock::genesis_block_hash(); TezBlock::new(number, timestamp, previous_hash) + .expect("Block creation should have succeeded") } #[test] -- GitLab From 5323be42de3e57e8bff8178f57b3690e598f4620 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 10 Apr 2025 17:53:27 +0200 Subject: [PATCH 3/9] Tezlink/Kernel: Implement rlp for operation to match the trait --- etherlink/kernel_latest/Cargo.lock | 1 + etherlink/kernel_latest/tezos/Cargo.toml | 2 + .../kernel_latest/tezos/src/operation.rs | 43 +++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/etherlink/kernel_latest/Cargo.lock b/etherlink/kernel_latest/Cargo.lock index f39004ae114d..84fcf2e53c32 100644 --- a/etherlink/kernel_latest/Cargo.lock +++ b/etherlink/kernel_latest/Cargo.lock @@ -2331,6 +2331,7 @@ dependencies = [ "nom", "num-bigint", "primitive-types", + "rlp", "tezos-smart-rollup", "tezos_crypto_rs", "tezos_data_encoding", diff --git a/etherlink/kernel_latest/tezos/Cargo.toml b/etherlink/kernel_latest/tezos/Cargo.toml index e3c64fdb2297..6c75b9dc52a4 100644 --- a/etherlink/kernel_latest/tezos/Cargo.toml +++ b/etherlink/kernel_latest/tezos/Cargo.toml @@ -18,3 +18,5 @@ tezos-smart-rollup.workspace = true num-bigint.workspace = true tezos_data_encoding.workspace = true nom.workspace = true + +rlp.workspace = true diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index 8da026ad6fbd..518964bcd3d4 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -9,6 +9,7 @@ use nom::combinator::map; use nom::error::{ErrorKind, ParseError}; use nom::{bytes::complete::take, Finish}; use primitive_types::H256; +use rlp::Decodable; use tezos_crypto_rs::hash::{HashType, UnknownSignature}; use tezos_data_encoding::types::Narith; use tezos_data_encoding::{ @@ -205,11 +206,31 @@ impl Operation { } } +impl Operation { + // The `rlp_append` function from the Encodable trait can't fail but `to_bytes` + // return a result. To avoid unwraping and risk a panic we're not implementing + // the trait exactly, but we expose a serialization function. + pub fn rlp_append(&self, s: &mut rlp::RlpStream) -> Result<(), BinError> { + let bytes = self.to_bytes()?; + s.append(&bytes); + Ok(()) + } +} + +impl Decodable for Operation { + fn decode(rlp: &rlp::Rlp) -> Result { + let raw: Vec = rlp.as_val()?; + Operation::try_from_bytes(&raw) + .map_err(|_| rlp::DecoderError::Custom("Operation::try_from_bytes failed")) + } +} + #[cfg(test)] mod tests { use super::{ManagerOperation, Operation, OperationContent}; use crate::block::TezBlock; use primitive_types::H256; + use rlp::{Decodable, Rlp, RlpStream}; use tezos_crypto_rs::{ hash::{HashType, UnknownSignature}, public_key::PublicKey, @@ -224,7 +245,7 @@ mod tests { // Public key hash in b58 for 0002298c03ed7d454a101eb7022bc95f7e5f41ac78 let source = PublicKeyHash::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") - .expect("Public key hash conversion should succeed"); + .expect("Public key hash conversion should succeeded"); Operation { branch, @@ -244,22 +265,36 @@ mod tests { let pk = PublicKey::from_b58check( "edpkuT1qccDweCHnvgjLuNUHERpZmEaFZfbWvTzj2BxmTgQBZjaDFD", ) - .expect("Public key creation should have succeed"); + .expect("Public key creation should have succeeded"); let signature = UnknownSignature::from_base58_check("sigSPESPpW4p44JK181SmFCFgZLVvau7wsJVN85bv5ciigMu7WSRnxs9H2NydN5ecxKHJBQTudFPrUccktoi29zHYsuzpzBX").unwrap(); make_dummy_operation(OperationContent::Reveal { pk }, signature) } + #[test] + fn operation_rlp_roundtrip() { + let operation = make_dummy_reveal_operation(); + let mut stream = RlpStream::new(); + operation + .rlp_append(&mut stream) + .expect("rlp_append should have succeeded"); + let bytes = stream.as_raw(); + let rlp = Rlp::new(bytes); + let decoded_operation = + Operation::decode(&rlp).expect("Decoding operation should have succeeded"); + assert_eq!(operation, decoded_operation); + } + #[test] fn test_reveal_operation_roundtrip_encoding() { let operation = make_dummy_reveal_operation(); let bytes = operation .to_bytes() - .expect("Encoding reveal operation should have succeed"); + .expect("Encoding reveal operation should have succeeded"); let operation_from_bytes = Operation::try_from_bytes(&bytes) - .expect("Decoding reveal operation should have succeed"); + .expect("Decoding reveal operation should have succeeded"); assert_eq!(operation, operation_from_bytes); } -- GitLab From 762af3d3b7e7ba8186bd17b7ff5f3aa0d74df804 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 17 Apr 2025 16:57:03 +0200 Subject: [PATCH 4/9] Tezlink/Kernel: Introduce operation in kernel --- etherlink/kernel_latest/kernel/src/block.rs | 2 +- etherlink/kernel_latest/kernel/src/chains.rs | 59 +++++++++++++++---- etherlink/kernel_latest/kernel/src/l2block.rs | 2 +- etherlink/kernel_latest/tezos/src/block.rs | 51 ++++++++++++++++ 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 078f480a3ef9..5bce4c6386ca 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -647,7 +647,7 @@ mod tests { fn tezlink_blueprint() -> Blueprint { Blueprint { - transactions: TezTransactions {}, + transactions: TezTransactions(vec![]), timestamp: Timestamp::from(0i64), } } diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index 99052b26729e..6940fec57020 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -10,6 +10,7 @@ use crate::{ DelayedTransactionFetchingResult, EVMBlockHeader, TezBlockHeader, }, delayed_inbox::DelayedInbox, + error, fees::MINIMUM_BASE_FEE_PER_GAS, l2block::L2Block, simulation::start_simulation_mode, @@ -25,12 +26,15 @@ use evm_execution::{ }; use primitive_types::{H160, H256, U256}; use rlp::{Decodable, Encodable}; -use std::fmt::{Debug, Display}; +use std::{ + collections::VecDeque, + fmt::{Debug, Display}, +}; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup::{outbox::OutboxQueue, types::Timestamp}; use tezos_smart_rollup_host::path::{Path, RefPath}; -use tezos_tezlink::block::TezBlock; +use tezos_tezlink::{block::TezBlock, operation::Operation}; pub const ETHERLINK_SAFE_STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); @@ -85,6 +89,8 @@ pub struct TezBlockInProgress { number: U256, timestamp: Timestamp, previous_hash: H256, + #[allow(dead_code)] + operations: VecDeque, } impl BlockInProgressTrait for TezBlockInProgress { @@ -116,26 +122,44 @@ impl TransactionsTrait for crate::transaction::Transactions { } #[derive(Debug)] -pub struct TezTransactions {} +pub struct TezTransactions(pub Vec); impl TransactionsTrait for TezTransactions { - fn extend(&mut self, _: Self) {} + fn extend(&mut self, other: Self) { + let TezTransactions(ref mut ops) = self; + let TezTransactions(other) = other; + ops.extend(other) + } fn number_of_txs(&self) -> usize { - 0 + let TezTransactions(operations) = self; + operations.len() } } impl Encodable for TezTransactions { fn rlp_append(&self, stream: &mut rlp::RlpStream) { - let Self {} = self; - stream.begin_list(0); + let Self(operations) = self; + stream.begin_list(operations.len()); + for op in operations { + // We don't want the kernel to panic if there's an error + // and we can't print a log as we don't have access to + // the host. So we just ignore the result. + let _ = op.rlp_append(stream); + } } } impl Decodable for TezTransactions { - fn decode(_decoder: &rlp::Rlp) -> Result { - Ok(Self {}) + fn decode(decoder: &rlp::Rlp) -> Result { + if !decoder.is_list() { + return Err(rlp::DecoderError::RlpExpectedToBeList); + } + let operations = decoder + .iter() + .map(|rlp| Operation::decode(&rlp)) + .collect::, rlp::DecoderError>>()?; + Ok(TezTransactions(operations)) } } @@ -398,10 +422,12 @@ impl ChainConfigTrait for MichelsonChainConfig { header: Self::ChainHeader, blueprint: Blueprint, ) -> Self::BlockInProgress { + let TezTransactions(operations) = blueprint.transactions; TezBlockInProgress { number: current_block_number, timestamp: blueprint.timestamp, previous_hash: header.hash, + operations: VecDeque::from(operations), } } @@ -413,15 +439,23 @@ impl ChainConfigTrait for MichelsonChainConfig { ) -> anyhow::Result<(DelayedTransactionFetchingResult, usize)> { Ok(( - DelayedTransactionFetchingResult::Ok(TezTransactions {}), + DelayedTransactionFetchingResult::Ok(TezTransactions(vec![])), current_blueprint_size, )) } fn transactions_from_bytes( - _bytes: Vec>, + bytes: Vec>, ) -> anyhow::Result { - Ok(TezTransactions {}) + let operations = bytes + .iter() + .map(|bytes| { + Operation::try_from_bytes(bytes).map_err(|decode_error| { + error::Error::NomReadError(format!("{:?}", decode_error)) + }) + }) + .collect::, error::Error>>()?; + Ok(TezTransactions(operations)) } fn read_block_in_progress( @@ -447,6 +481,7 @@ impl ChainConfigTrait for MichelsonChainConfig { number, timestamp, previous_hash, + operations: _, } = block_in_progress; log!( host, diff --git a/etherlink/kernel_latest/kernel/src/l2block.rs b/etherlink/kernel_latest/kernel/src/l2block.rs index b49afba7f75c..c53f0b706f11 100644 --- a/etherlink/kernel_latest/kernel/src/l2block.rs +++ b/etherlink/kernel_latest/kernel/src/l2block.rs @@ -37,7 +37,7 @@ impl L2Block { pub fn number_of_transactions(&self) -> usize { match &self { Self::Etherlink(block) => block.transactions.len(), - Self::Tezlink(_) => 0, + Self::Tezlink(block) => block.operations.len(), } } diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs index 7bc12b7a93ac..da7af80eaa80 100644 --- a/etherlink/kernel_latest/tezos/src/block.rs +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -2,12 +2,14 @@ // // SPDX-License-Identifier: MIT +use crate::{operation::Operation, operation_result::OperationResultSum}; use nom::bytes::complete::take; use nom::combinator::map; use nom::error::ParseError; use nom::Finish; use primitive_types::{H256, U256}; use tezos_crypto_rs::blake2b::digest_256; +use tezos_crypto_rs::hash::HashType; use tezos_data_encoding::enc as tezos_enc; use tezos_data_encoding::nom as tezos_nom; use tezos_data_encoding::nom::error::DecodeError; @@ -16,6 +18,45 @@ use tezos_enc::{BinError, BinWriter}; use tezos_nom::{NomReader, NomResult}; use tezos_smart_rollup::types::Timestamp; +#[derive(PartialEq, Debug)] +pub struct AppliedOperation { + // OperationHash are 32 bytes long + pub hash: H256, + pub data: Operation, + pub receipt: OperationResultSum, +} + +impl NomReader<'_> for AppliedOperation { + fn nom_read(input: &'_ [u8]) -> NomResult<'_, Self> { + // OperationHash are 32 bytes long + let size = HashType::OperationHash.size(); + let (input, hash) = + map(take::(size), H256::from_slice)(input)?; + let (input, operation) = Operation::nom_read(input)?; + let (input, receipt) = OperationResultSum::nom_read(input)?; + let applied_op = Self { + hash, + data: operation, + receipt, + }; + Ok((input, applied_op)) + } +} + +impl BinWriter for AppliedOperation { + fn bin_write(&self, output: &mut Vec) -> tezos_enc::BinResult { + let Self { + hash, + data, + receipt, + } = self; + tezos_enc::put_bytes(hash.as_bytes(), output); + data.bin_write(output)?; + receipt.bin_write(output)?; + Ok(()) + } +} + // WIP: This structure will evolve to look like Tezos block #[derive(PartialEq, Debug)] pub struct TezBlock { @@ -23,6 +64,7 @@ pub struct TezBlock { pub number: U256, pub timestamp: Timestamp, pub previous_hash: H256, + pub operations: Vec, } impl NomReader<'_> for TezBlock { @@ -40,6 +82,9 @@ impl NomReader<'_> for TezBlock { let (remaining, timestamp) = nom::number::complete::be_i64(remaining)?; let timestamp = Timestamp::from(timestamp); + let (remaining, operations) = + tezos_nom::dynamic(tezos_nom::list(AppliedOperation::nom_read))(remaining)?; + Ok(( remaining, Self { @@ -47,6 +92,7 @@ impl NomReader<'_> for TezBlock { number, timestamp, previous_hash, + operations, }, )) } @@ -61,12 +107,16 @@ impl BinWriter for TezBlock { number, timestamp, previous_hash, + operations, } = self; // Encode all block fields tezos_enc::put_bytes(&hash.to_fixed_bytes(), output); tezos_enc::u32(&number.as_u32(), output)?; tezos_enc::put_bytes(&previous_hash.to_fixed_bytes(), output); tezos_enc::i64(×tamp.i64(), output)?; + tezos_enc::dynamic(tezos_enc::list(AppliedOperation::bin_write))( + operations, output, + )?; Ok(()) } } @@ -101,6 +151,7 @@ impl TezBlock { number, timestamp, previous_hash, + operations: vec![], }; Ok(Self { hash: block.hash()?, -- GitLab From 2b1a39284befeb8a5442fdc062745d88a855bf1e Mon Sep 17 00:00:00 2001 From: arnaud Date: Wed, 28 May 2025 10:49:11 +0200 Subject: [PATCH 5/9] Tezlink/Node: Add operations in tezlink block in the node Original commit from @rafoo_ --- etherlink/bin_node/lib_dev/encodings/l2_types.ml | 14 ++++++++------ etherlink/bin_node/lib_dev/encodings/l2_types.mli | 1 + .../bin_node/test/test_blueprint_roundtrip.ml | 13 +++++++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/etherlink/bin_node/lib_dev/encodings/l2_types.ml b/etherlink/bin_node/lib_dev/encodings/l2_types.ml index dc04842d7636..6d4253e8d462 100644 --- a/etherlink/bin_node/lib_dev/encodings/l2_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/l2_types.ml @@ -53,6 +53,7 @@ module Tezos_block = struct level : int32; timestamp : Time.Protocol.t; parent_hash : Ethereum_types.block_hash; + operations : bytes; (* TODO: #7928 decode operations with receipts *) } let decode_block_hash = Ethereum_types.decode_block_hash @@ -77,15 +78,16 @@ module Tezos_block = struct in def "tezlink_block" @@ conv - (fun {hash; level; parent_hash; timestamp} -> - (hash, level, parent_hash, timestamp)) - (fun (hash, level, parent_hash, timestamp) -> - {hash; level; parent_hash; timestamp}) - (obj4 + (fun {hash; level; parent_hash; timestamp; operations} -> + (hash, level, parent_hash, timestamp, operations)) + (fun (hash, level, parent_hash, timestamp, operations) -> + {hash; level; parent_hash; timestamp; operations}) + (obj5 (req "hash" block_hash_encoding) (req "level" int32) (req "parent_hash" block_hash_encoding) - (req "timestamp" timestamp_encoding)) + (req "timestamp" timestamp_encoding) + (req "operations" bytes)) let () = Data_encoding.Registration.register block_encoding diff --git a/etherlink/bin_node/lib_dev/encodings/l2_types.mli b/etherlink/bin_node/lib_dev/encodings/l2_types.mli index 865f7fdc6ac7..b13fa1225e04 100644 --- a/etherlink/bin_node/lib_dev/encodings/l2_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/l2_types.mli @@ -50,6 +50,7 @@ module Tezos_block : sig level : int32; timestamp : Time.Protocol.t; parent_hash : Ethereum_types.block_hash; + operations : bytes; } val decode_block_hash : bytes -> Ethereum_types.block_hash diff --git a/etherlink/bin_node/test/test_blueprint_roundtrip.ml b/etherlink/bin_node/test/test_blueprint_roundtrip.ml index 5385a6b3202d..ea7ab5fcf7b6 100644 --- a/etherlink/bin_node/test/test_blueprint_roundtrip.ml +++ b/etherlink/bin_node/test/test_blueprint_roundtrip.ml @@ -78,7 +78,14 @@ let make_blueprint ~delayed_transactions ~transactions = let make_tez_block ~level ~timestamp ~parent_hash () = let block_without_hash = - L2_types.Tezos_block.{level; hash = zero_hash; timestamp; parent_hash} + L2_types.Tezos_block. + { + level; + hash = zero_hash; + timestamp; + parent_hash; + operations = Bytes.empty; + } in let block_bytes = Bytes.of_string @@ -89,7 +96,9 @@ let make_tez_block ~level ~timestamp ~parent_hash () = let hash = Ethereum_types.decode_block_hash (Block_hash.to_bytes block_hash) in - return L2_types.Tezos_block.{level; hash; timestamp; parent_hash} + return + L2_types.Tezos_block. + {level; hash; timestamp; parent_hash; operations = Bytes.empty} let test_blueprint_roundtrip ~title ~delayed_transactions ~transactions () = register ~title:(sf "Blueprint producer decoder roundtrip (%s)" title) -- GitLab From a4936811e3ce2ab5395e58757acf9c715078720a Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 10 Apr 2025 15:52:17 +0200 Subject: [PATCH 6/9] Tezlink/Kernel: Add operations application when computing a BlockInProgress --- etherlink/kernel_latest/kernel/src/chains.rs | 38 +++++++++++++++++-- etherlink/kernel_latest/kernel/src/error.rs | 3 ++ etherlink/kernel_latest/tezos/src/block.rs | 5 ++- .../kernel_latest/tezos/src/operation.rs | 7 ++++ 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index 6940fec57020..60b26ee09bb4 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -32,9 +32,13 @@ use std::{ }; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; +use tezos_execution::context; use tezos_smart_rollup::{outbox::OutboxQueue, types::Timestamp}; use tezos_smart_rollup_host::path::{Path, RefPath}; -use tezos_tezlink::{block::TezBlock, operation::Operation}; +use tezos_tezlink::{ + block::{AppliedOperation, TezBlock}, + operation::Operation, +}; pub const ETHERLINK_SAFE_STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); @@ -89,7 +93,7 @@ pub struct TezBlockInProgress { number: U256, timestamp: Timestamp, previous_hash: H256, - #[allow(dead_code)] + applied: Vec, operations: VecDeque, } @@ -427,6 +431,7 @@ impl ChainConfigTrait for MichelsonChainConfig { number: current_block_number, timestamp: blueprint.timestamp, previous_hash: header.hash, + applied: vec![], operations: VecDeque::from(operations), } } @@ -481,7 +486,8 @@ impl ChainConfigTrait for MichelsonChainConfig { number, timestamp, previous_hash, - operations: _, + mut applied, + mut operations, } = block_in_progress; log!( host, @@ -490,7 +496,31 @@ impl ChainConfigTrait for MichelsonChainConfig { number ); - let tezblock = TezBlock::new(number, timestamp, previous_hash)?; + let context = context::Context::from(&self.storage_root_path())?; + + // Compute operations that are in the block in progress + while !operations.is_empty() { + // Retrieve the next operation in the VecDequeue + let operation = operations.pop_front().ok_or(error::Error::Reboot)?; + + // Try to apply the operation with the tezos_execution crate, return a receipt + // on whether it failed or not + let receipt = tezos_execution::apply_operation(host, &context, &operation)?; + + // Compute the hash of the operation + let hash = operation.hash()?; + + // Add the applied operation in the block in progress + let applied_operation = AppliedOperation { + hash, + data: operation, + receipt, + }; + applied.push(applied_operation); + } + + // Create a Tezos block from the block in progess + let tezblock = TezBlock::new(number, timestamp, previous_hash, applied)?; let new_block = L2Block::Tezlink(tezblock); let root = self.storage_root_path(); crate::block_storage::store_current(host, &root, &new_block) diff --git a/etherlink/kernel_latest/kernel/src/error.rs b/etherlink/kernel_latest/kernel/src/error.rs index 3684320a3ced..84bcb3b1bcfd 100644 --- a/etherlink/kernel_latest/kernel/src/error.rs +++ b/etherlink/kernel_latest/kernel/src/error.rs @@ -11,6 +11,7 @@ use primitive_types::U256; use rlp::DecoderError; use tezos_data_encoding::enc::BinError; use tezos_ethereum::tx_common::SigError; +use tezos_execution::ApplyKernelError; use tezos_indexable_storage::IndexableStorageError; use tezos_smart_rollup_encoding::entrypoint::EntrypointError; use tezos_smart_rollup_encoding::michelson::ticket::TicketError; @@ -90,6 +91,8 @@ pub enum Error { #[error(transparent)] Transfer(TransferError), #[error(transparent)] + Operation(ApplyKernelError), + #[error(transparent)] Storage(StorageError), #[error("Invalid conversion")] InvalidConversion, diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs index da7af80eaa80..765f032e2e5b 100644 --- a/etherlink/kernel_latest/tezos/src/block.rs +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -145,13 +145,14 @@ impl TezBlock { number: U256, timestamp: Timestamp, previous_hash: H256, + operations: Vec, ) -> Result { let block = Self { hash: H256::zero(), number, timestamp, previous_hash, - operations: vec![], + operations, }; Ok(Self { hash: block.hash()?, @@ -197,7 +198,7 @@ mod tests { let number = U256::one(); let timestamp = Timestamp::from(0); let previous_hash = TezBlock::genesis_block_hash(); - TezBlock::new(number, timestamp, previous_hash) + TezBlock::new(number, timestamp, previous_hash, vec![]) .expect("Block creation should have succeeded") } diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index 518964bcd3d4..79838252b291 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -10,6 +10,7 @@ use nom::error::{ErrorKind, ParseError}; use nom::{bytes::complete::take, Finish}; use primitive_types::H256; use rlp::Decodable; +use tezos_crypto_rs::blake2b::digest_256; use tezos_crypto_rs::hash::{HashType, UnknownSignature}; use tezos_data_encoding::types::Narith; use tezos_data_encoding::{ @@ -215,6 +216,12 @@ impl Operation { s.append(&bytes); Ok(()) } + + pub fn hash(&self) -> Result { + let serialized_op = self.to_bytes()?; + let op_hash = digest_256(&serialized_op); + Ok(H256::from_slice(&op_hash)) + } } impl Decodable for Operation { -- GitLab From 9b3f67d74465f183019df64d66be4ef89c52a5fb Mon Sep 17 00:00:00 2001 From: arnaud Date: Mon, 2 Jun 2025 16:55:50 +0200 Subject: [PATCH 7/9] Tezlink/Tests: Move operation builder outside of test module --- .../kernel_latest/tezos/src/operation.rs | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index 79838252b291..7e56cdcd5a0a 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -232,10 +232,49 @@ impl Decodable for Operation { } } +#[cfg(test)] +fn make_dummy_operation( + operation: OperationContent, + signature: UnknownSignature, +) -> Operation { + use crate::block::TezBlock; + + let branch = TezBlock::genesis_block_hash(); + + // Public key hash in b58 for 0002298c03ed7d454a101eb7022bc95f7e5f41ac78 + let source = PublicKeyHash::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Public key hash conversion should succeeded"); + + Operation { + branch, + content: ManagerOperation { + source, + fee: 1_u64.into(), + counter: 10_u64.into(), + gas_limit: 68_u64.into(), + storage_limit: 45_u64.into(), + operation, + }, + signature, + } +} + +#[cfg(test)] +pub fn make_dummy_reveal_operation() -> Operation { + let pk = PublicKey::from_b58check( + "edpkuT1qccDweCHnvgjLuNUHERpZmEaFZfbWvTzj2BxmTgQBZjaDFD", + ) + .expect("Public key creation should have succeeded"); + + let signature = UnknownSignature::from_base58_check("sigSPESPpW4p44JK181SmFCFgZLVvau7wsJVN85bv5ciigMu7WSRnxs9H2NydN5ecxKHJBQTudFPrUccktoi29zHYsuzpzBX").unwrap(); + + make_dummy_operation(OperationContent::Reveal { pk }, signature) +} + #[cfg(test)] mod tests { use super::{ManagerOperation, Operation, OperationContent}; - use crate::block::TezBlock; + use crate::operation::make_dummy_reveal_operation; use primitive_types::H256; use rlp::{Decodable, Rlp, RlpStream}; use tezos_crypto_rs::{ @@ -244,41 +283,6 @@ mod tests { }; use tezos_smart_rollup::types::PublicKeyHash; - fn make_dummy_operation( - operation: OperationContent, - signature: UnknownSignature, - ) -> Operation { - let branch = TezBlock::genesis_block_hash(); - - // Public key hash in b58 for 0002298c03ed7d454a101eb7022bc95f7e5f41ac78 - let source = PublicKeyHash::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") - .expect("Public key hash conversion should succeeded"); - - Operation { - branch, - content: ManagerOperation { - source, - fee: 1_u64.into(), - counter: 10_u64.into(), - gas_limit: 68_u64.into(), - storage_limit: 45_u64.into(), - operation, - }, - signature, - } - } - - fn make_dummy_reveal_operation() -> Operation { - let pk = PublicKey::from_b58check( - "edpkuT1qccDweCHnvgjLuNUHERpZmEaFZfbWvTzj2BxmTgQBZjaDFD", - ) - .expect("Public key creation should have succeeded"); - - let signature = UnknownSignature::from_base58_check("sigSPESPpW4p44JK181SmFCFgZLVvau7wsJVN85bv5ciigMu7WSRnxs9H2NydN5ecxKHJBQTudFPrUccktoi29zHYsuzpzBX").unwrap(); - - make_dummy_operation(OperationContent::Reveal { pk }, signature) - } - #[test] fn operation_rlp_roundtrip() { let operation = make_dummy_reveal_operation(); -- GitLab From d9c5b83ee42f0b9269e32c885886e3e0f3cd1c61 Mon Sep 17 00:00:00 2001 From: arnaud Date: Mon, 2 Jun 2025 17:02:28 +0200 Subject: [PATCH 8/9] Tezos/Kernel/Tests: Roundtrip test Tezlink block with operations --- etherlink/kernel_latest/tezos/src/block.rs | 36 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs index 765f032e2e5b..5f4b3b3c5dc3 100644 --- a/etherlink/kernel_latest/tezos/src/block.rs +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -180,10 +180,12 @@ impl TezBlock { #[cfg(test)] mod tests { - use primitive_types::U256; + use primitive_types::{H256, U256}; use tezos_smart_rollup::types::Timestamp; - use super::TezBlock; + use crate::operation_result::{OperationResult, OperationResultSum, RevealSuccess}; + + use super::{AppliedOperation, TezBlock}; pub fn block_roundtrip(block: TezBlock) { let bytes = block @@ -194,16 +196,40 @@ mod tests { assert_eq!(block, decoded_block, "Roundtrip failed on {:?}", block) } - fn dummy_tezblock() -> TezBlock { + fn dummy_applied_operation() -> AppliedOperation { + let hash = H256::random(); + let data = crate::operation::make_dummy_reveal_operation(); + let receipt = OperationResultSum::Reveal(OperationResult { + balance_updates: vec![], + result: crate::operation_result::ContentResult::Applied(RevealSuccess { + consumed_gas: 0u64.into(), + }), + }); + AppliedOperation { + hash, + data, + receipt, + } + } + + fn dummy_tezblock(operations: Vec) -> TezBlock { let number = U256::one(); let timestamp = Timestamp::from(0); let previous_hash = TezBlock::genesis_block_hash(); - TezBlock::new(number, timestamp, previous_hash, vec![]) + TezBlock::new(number, timestamp, previous_hash, operations) .expect("Block creation should have succeeded") } + #[test] + fn test_empty_block_rlp_roundtrip() { + block_roundtrip(dummy_tezblock(vec![])); + } + #[test] fn test_block_rlp_roundtrip() { - block_roundtrip(dummy_tezblock()); + block_roundtrip(dummy_tezblock(vec![ + dummy_applied_operation(), + dummy_applied_operation(), + ])); } } -- GitLab From f33fa26b0b4f287e67d3c9193a4a44cadde6770a Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 10 Apr 2025 17:56:00 +0200 Subject: [PATCH 9/9] Tezlink/Tests: Test a reveal operation in a block and that the revealed is applied --- etherlink/kernel_latest/Cargo.lock | 1 + etherlink/kernel_latest/kernel/Cargo.toml | 1 + etherlink/kernel_latest/kernel/src/block.rs | 105 ++++++++++++++++++-- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/etherlink/kernel_latest/Cargo.lock b/etherlink/kernel_latest/Cargo.lock index 84fcf2e53c32..f6b4442b05f0 100644 --- a/etherlink/kernel_latest/Cargo.lock +++ b/etherlink/kernel_latest/Cargo.lock @@ -780,6 +780,7 @@ dependencies = [ "getrandom 0.2.15", "hex", "libsecp256k1", + "num-bigint", "num-derive", "num-traits", "pretty_assertions", diff --git a/etherlink/kernel_latest/kernel/Cargo.toml b/etherlink/kernel_latest/kernel/Cargo.toml index ce106f29f148..efa52a466231 100644 --- a/etherlink/kernel_latest/kernel/Cargo.toml +++ b/etherlink/kernel_latest/kernel/Cargo.toml @@ -55,6 +55,7 @@ tezos-smart-rollup-debug.workspace = true tezos-smart-rollup-encoding.workspace = true tezos-smart-rollup-installer-config.workspace = true tezos-smart-rollup-storage.workspace = true +num-bigint.workspace = true tezos_data_encoding.workspace = true diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 5bce4c6386ca..b018027f5561 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -601,7 +601,10 @@ mod tests { use crate::blueprint::Blueprint; use crate::blueprint_storage::store_inbox_blueprint; use crate::blueprint_storage::store_inbox_blueprint_by_number; - use crate::chains::{EvmChainConfig, MichelsonChainConfig, TezTransactions}; + use crate::chains::{ + EvmChainConfig, MichelsonChainConfig, TezTransactions, + TEZLINK_SAFE_STORAGE_ROOT_PATH, + }; use crate::fees::DA_FEE_PER_BYTE; use crate::fees::MINIMUM_BASE_FEE_PER_GAS; use crate::storage::read_block_in_progress; @@ -620,6 +623,7 @@ mod tests { use evm_execution::precompiles::precompile_set; use primitive_types::{H160, U256}; use std::str::FromStr; + use tezos_crypto_rs::hash::UnknownSignature; use tezos_ethereum::block::BlockFees; use tezos_ethereum::transaction::{ TransactionHash, TransactionStatus, TransactionType, TRANSACTION_HASH_SIZE, @@ -628,6 +632,12 @@ mod tests { use tezos_evm_runtime::extensions::WithGas; use tezos_evm_runtime::runtime::MockKernelHost; use tezos_evm_runtime::runtime::Runtime; + use tezos_execution::account_storage::Manager; + use tezos_execution::account_storage::TezlinkImplicitAccount; + use tezos_execution::context; + use tezos_smart_rollup::types::Contract; + use tezos_smart_rollup::types::PublicKey; + use tezos_smart_rollup::types::PublicKeyHash; use tezos_smart_rollup_encoding::timestamp::Timestamp; use tezos_smart_rollup_host::path::concat; use tezos_smart_rollup_host::path::RefPath; @@ -637,6 +647,34 @@ mod tests { } use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; + use tezos_tezlink::operation::ManagerOperation; + use tezos_tezlink::operation::Operation; + use tezos_tezlink::operation::OperationContent; + + pub fn make_reveal_operation( + fee: u64, + counter: u64, + gas_limit: u64, + storage_limit: u64, + source: PublicKeyHash, + pk: PublicKey, + ) -> Operation { + let branch = tezos_tezlink::block::TezBlock::genesis_block_hash(); + // No need a real signature for now + let signature = UnknownSignature::from_base58_check("sigSPESPpW4p44JK181SmFCFgZLVvau7wsJVN85bv5ciigMu7WSRnxs9H2NydN5ecxKHJBQTudFPrUccktoi29zHYsuzpzBX").unwrap(); + Operation { + branch, + content: ManagerOperation { + source, + fee: fee.into(), + counter: counter.into(), + operation: OperationContent::Reveal { pk }, + gas_limit: gas_limit.into(), + storage_limit: storage_limit.into(), + }, + signature, + } + } fn blueprint(transactions: Vec) -> Blueprint { Blueprint { @@ -645,9 +683,9 @@ mod tests { } } - fn tezlink_blueprint() -> Blueprint { + fn tezlink_blueprint(operations: Vec) -> Blueprint { Blueprint { - transactions: TezTransactions(vec![]), + transactions: TezTransactions(operations), timestamp: Timestamp::from(0i64), } } @@ -927,9 +965,9 @@ mod tests { store_blueprints::<_, MichelsonChainConfig>( &mut host, vec![ - tezlink_blueprint(), - tezlink_blueprint(), - tezlink_blueprint(), + tezlink_blueprint(vec![]), + tezlink_blueprint(vec![]), + tezlink_blueprint(vec![]), ], ); @@ -945,6 +983,61 @@ mod tests { assert_eq!(U256::from(2), read_current_number(&host).unwrap()); } + #[test] + // Test if tezlink block production works with a reveal operation + fn test_produce_tezlink_block_with_reveal_operation() { + let mut host = MockKernelHost::default(); + + let chain_config = dummy_tez_config(); + let mut config = dummy_configuration(); + + let contract = Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Contract creation should have succeeded"); + + let context = context::Context::from(&TEZLINK_SAFE_STORAGE_ROOT_PATH) + .expect("Context creation should have succeeded"); + + let account = TezlinkImplicitAccount::from_contract(&context, &contract) + .expect("Account interface should be correct"); + + TezlinkImplicitAccount::allocate(&mut host, &context, &contract) + .expect("Contract initialization should have succeeded"); + + let src = PublicKeyHash::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("PublicKeyHash b58 conversion should have succeeded"); + + let pk = PublicKey::from_b58check( + "edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav", + ) + .expect("Public key creation should have succeeded"); + + let manager = account + .manager(&host) + .expect("Retrieve manager should have succeeded"); + + assert_eq!(Manager::NotRevealed(src.clone()), manager); + + let operation = make_reveal_operation(0, 1, 0, 0, src, pk.clone()); + + store_blueprints::<_, MichelsonChainConfig>( + &mut host, + vec![tezlink_blueprint(vec![operation])], + ); + + produce(&mut host, &chain_config, &mut config, None, None) + .expect("The block production should have succeeded."); + let computation = produce(&mut host, &chain_config, &mut config, None, None) + .expect("The block production should have succeeded."); + assert_eq!(ComputationResult::Finished, computation); + assert_eq!(U256::from(0), read_current_number(&host).unwrap()); + + let manager = account + .manager(&host) + .expect("Retrieve manager should have succeeded"); + + assert_eq!(Manager::Revealed(pk), manager); + } + #[test] // Test if the invalid transactions are producing receipts fn test_invalid_transactions_receipt_status() { -- GitLab