From 7d1fa52eb8e14ce77380acf8de9f5674ad26e9ed Mon Sep 17 00:00:00 2001 From: Luciano Freitas Date: Fri, 11 Jul 2025 14:07:22 +0200 Subject: [PATCH 1/4] Tezlink/Kernel: propagate clone trait --- .../kernel_latest/tezos/src/operation.rs | 1 + .../tezos/src/operation_result.rs | 40 +++++++++---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index 3da5e11c109a..aa3971f495a1 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -43,6 +43,7 @@ pub struct TransferContent { pub parameters: Option, } +#[derive(Clone)] pub enum OperationContent { Reveal(RevealContent), Transfer(TransferContent), diff --git a/etherlink/kernel_latest/tezos/src/operation_result.rs b/etherlink/kernel_latest/tezos/src/operation_result.rs index 7a828bb792d7..5d090baaf7da 100644 --- a/etherlink/kernel_latest/tezos/src/operation_result.rs +++ b/etherlink/kernel_latest/tezos/src/operation_result.rs @@ -22,13 +22,13 @@ use thiserror::Error; use crate::operation::ManagerOperationContent; -#[derive(Debug, PartialEq, Eq, NomReader, BinWriter)] +#[derive(Debug, PartialEq, Eq, NomReader, BinWriter, Clone)] pub struct CounterError { pub expected: Narith, pub found: Narith, } -#[derive(Error, Debug, PartialEq, Eq, NomReader, BinWriter)] +#[derive(Error, Debug, PartialEq, Eq, NomReader, BinWriter, Clone)] pub enum ValidityError { #[error("Counter in the past: {0:?}.")] CounterInThePast(CounterError), @@ -64,7 +64,7 @@ pub enum ValidityError { FailedToIncrementCounter, } -#[derive(Error, Debug, PartialEq, Eq, NomReader, BinWriter)] +#[derive(Error, Debug, PartialEq, Eq, NomReader, BinWriter, Clone)] pub enum RevealError { #[error("Revelation failed because the public key {0} was already revealed.")] PreviouslyRevealedKey(PublicKey), @@ -78,7 +78,7 @@ pub enum RevealError { FailedToWriteManager, } -#[derive(thiserror::Error, Debug, PartialEq, Eq, BinWriter, NomReader)] +#[derive(thiserror::Error, Debug, PartialEq, Eq, BinWriter, NomReader, Clone)] #[error("{contract:?} cannot spend {amount:?} as its balance is {balance:?}")] pub struct BalanceTooLow { pub contract: Contract, @@ -86,7 +86,7 @@ pub struct BalanceTooLow { pub amount: Narith, } -#[derive(Error, Debug, PartialEq, Eq, BinWriter, NomReader)] +#[derive(Error, Debug, PartialEq, Eq, BinWriter, NomReader, Clone)] pub enum TransferError { #[error(transparent)] BalanceTooLow(BalanceTooLow), @@ -145,7 +145,7 @@ impl From for TransferError { } } -#[derive(Error, Debug, PartialEq, Eq, NomReader)] +#[derive(Clone, Error, Debug, PartialEq, Eq, NomReader)] pub enum ApplyOperationError { #[error("Reveal error: {0}")] Reveal(#[from] RevealError), @@ -155,7 +155,7 @@ pub enum ApplyOperationError { UnSupportedOperation(String), } -#[derive(Error, Debug, PartialEq, Eq)] +#[derive(Error, Debug, PartialEq, Eq, Clone)] pub enum OperationError { #[error("Validation error: {0}")] Validation(#[from] ValidityError), @@ -230,15 +230,15 @@ impl NomReader<'_> for OperationError { } pub trait OperationKind { - type Success: PartialEq + Debug + BinWriter + for<'a> NomReader<'a>; + type Success: PartialEq + Debug + BinWriter + Clone + for<'a> NomReader<'a>; } /// Empty struct to implement [OperationKind] trait for Reveal -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub struct Reveal; /// Empty struct to implement [OperationKind] trait for Transfer -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub struct Transfer; impl OperationKind for Transfer { @@ -250,12 +250,12 @@ impl OperationKind for Reveal { } // Inspired from `src/proto_alpha/lib_protocol/apply_results.ml` : transaction_contract_variant_cases -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] pub enum TransferTarget { ToContrat(TransferSuccess), } -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] pub struct RevealSuccess { pub consumed_gas: Narith, } @@ -276,7 +276,7 @@ impl NomReader<'_> for Empty { } } -#[derive(PartialEq, Debug, BinWriter, NomReader)] +#[derive(PartialEq, Debug, BinWriter, NomReader, Clone)] pub struct TransferSuccess { pub storage: Option>, #[encoding(dynamic, list)] @@ -298,7 +298,7 @@ pub struct TransferSuccess { // An operation error in a Tezos receipt has no specific format // It should just be encoded as a JSON, so we can't derive // NomReader and BinWriter if we want to be Tezos compatible -#[derive(PartialEq, Debug, BinWriter, NomReader)] +#[derive(PartialEq, Debug, BinWriter, NomReader, Clone)] pub struct ApplyOperationErrors { #[encoding(dynamic, list)] pub errors: Vec, @@ -312,7 +312,7 @@ impl From> for ApplyOperationErrors { // Inspired from `operation_result` in `src/proto_alpha/lib_protocol/apply_operation_result.ml` // Still need to implement Backtracked and Skipped -#[derive(PartialEq, Debug, BinWriter, NomReader)] +#[derive(PartialEq, Debug, BinWriter, NomReader, Clone)] pub enum ContentResult { Applied(M::Success), Failed(ApplyOperationErrors), @@ -320,7 +320,7 @@ pub enum ContentResult { /// A [Balance] updates can be triggered on different target /// inspired from src/proto_alpha/lib_protocol/receipt_repr.ml -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] #[encoding(tags = "u8")] pub enum Balance { #[encoding(tag = 0)] @@ -332,14 +332,14 @@ pub enum Balance { } /// Inspired from update_origin_encoding src/proto_alpha/lib_protocol/receipt_repr.ml -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] pub enum UpdateOrigin { BlockApplication, } /// Depending of the sign of [changes], the balance is credited or debited /// Inspired from balance_updates_encoding src/proto_alpha/lib_protocol/receipt_repr.ml -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] pub struct BalanceUpdate { pub balance: Balance, pub changes: i64, @@ -349,7 +349,7 @@ pub struct BalanceUpdate { // Inspired from `Manager_operation_result` case in 'kind contents_result type // from `src/proto_alpha/lib_protocol/apply_results.ml` file. // Still need to implement internal_results -#[derive(PartialEq, Debug, NomReader, BinWriter)] +#[derive(PartialEq, Debug, NomReader, BinWriter, Clone)] pub struct OperationResult { #[encoding(dynamic, list)] pub balance_updates: Vec, @@ -358,7 +358,7 @@ pub struct OperationResult { #[encoding(dynamic, bytes)] pub internal_operation_results: Vec, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub enum OperationResultSum { Reveal(OperationResult), Transfer(OperationResult), -- GitLab From 73dca7b3a3fa0ec63227852bb828c32cdb0a4de7 Mon Sep 17 00:00:00 2001 From: Luciano Freitas Date: Fri, 1 Aug 2025 15:00:25 +0200 Subject: [PATCH 2/4] Tezlink/Kernel/Execution: break operation down before passing args --- .../kernel_latest/tezos_execution/src/lib.rs | 19 ++++++++++++++++--- .../tezos_execution/src/validate.rs | 17 ++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs index 2ca96747cdeb..847433cbd330 100644 --- a/etherlink/kernel_latest/tezos_execution/src/lib.rs +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -13,11 +13,13 @@ use mir::{ }; use num_bigint::{BigInt, BigUint}; use num_traits::ops::checked::CheckedSub; +use tezos_crypto_rs::hash::UnknownSignature; use tezos_crypto_rs::PublicKeyWithHash; use tezos_data_encoding::types::Narith; use tezos_evm_logging::{log, Level::*, Verbosity}; use tezos_evm_runtime::{runtime::Runtime, safe_storage::SafeStorage}; use tezos_smart_rollup::types::{Contract, PublicKey, PublicKeyHash}; +use tezos_tezlink::enc_wrappers::BlockHash; use tezos_tezlink::operation::Operation; use tezos_tezlink::operation_result::TransferTarget; use tezos_tezlink::{ @@ -447,9 +449,12 @@ fn execute_smart_contract<'a>( fn execute_validation( host: &mut Host, context: &Context, - operation: &Operation, + branch: &BlockHash, + content: &ManagerOperation, + signature: UnknownSignature, ) -> Result { - let mut validation_info = validate::validate_operation(host, context, operation)?; + let mut validation_info = + validate::validate_operation(host, context, branch, content, signature)?; validation_info .source_account @@ -469,8 +474,10 @@ pub fn validate_and_apply_operation( context: &context::Context, operation: Operation, ) -> Result { + let branch = operation.branch; let manager_operation: ManagerOperation = operation.content.clone().into(); + let signature = operation.signature; let source = &manager_operation.source; @@ -490,7 +497,13 @@ pub fn validate_and_apply_operation( log!(safe_host, Debug, "Verifying that the operation is valid"); - let validation_info = match execute_validation(&mut safe_host, context, &operation) { + let validation_info = match execute_validation( + &mut safe_host, + context, + &branch, + &manager_operation, + signature, + ) { Ok(validation_info) => validation_info, Err(validity_err) => { safe_host.revert()?; diff --git a/etherlink/kernel_latest/tezos_execution/src/validate.rs b/etherlink/kernel_latest/tezos_execution/src/validate.rs index 888eae39a654..df5687d64289 100644 --- a/etherlink/kernel_latest/tezos_execution/src/validate.rs +++ b/etherlink/kernel_latest/tezos_execution/src/validate.rs @@ -2,14 +2,14 @@ // // SPDX-License-Identifier: MIT +use tezos_crypto_rs::hash::UnknownSignature; use tezos_data_encoding::types::Narith; use tezos_evm_logging::log; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup::types::PublicKey; +use tezos_tezlink::enc_wrappers::BlockHash; use tezos_tezlink::{ - operation::{ - verify_signature, ManagerOperation, Operation, OperationContent, RevealContent, - }, + operation::{verify_signature, ManagerOperation, OperationContent, RevealContent}, operation_result::{CounterError, ValidityError}, }; @@ -129,11 +129,10 @@ pub struct ValidationInfo { pub fn validate_operation( host: &Host, context: &Context, - operation: &Operation, + branch: &BlockHash, + content: &ManagerOperation, + signature: UnknownSignature, ) -> Result { - let branch = &operation.branch; - let content: ManagerOperation = operation.content.clone().into(); - let signature = &operation.signature; let account = TezlinkImplicitAccount::from_public_key_hash(context, &content.source) .map_err(|_| ValidityError::FailedToFetchAccount)?; @@ -164,12 +163,12 @@ pub fn validate_operation( tezos_evm_logging::Level::Debug, "Invalid operation: Can't pay the fees" ); - return Err(ValidityError::CantPayFees(content.fee)); + return Err(ValidityError::CantPayFees(content.fee.clone())); } Err(_) => return Err(ValidityError::FailedToFetchBalance), }; - match verify_signature(&pk, branch, &operation.content, signature.clone()) { + match verify_signature(&pk, branch, &(content.clone().into()), signature.clone()) { Ok(true) => (), _ => return Err(ValidityError::InvalidSignature), } -- GitLab From 25a0247d641bd81fcffd48c9a341df91aff234e3 Mon Sep 17 00:00:00 2001 From: Luciano Freitas Date: Mon, 4 Aug 2025 08:42:59 +0200 Subject: [PATCH 3/4] Tezlink/Kernel: serialize batches --- etherlink/kernel_latest/tezos/src/operation.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index aa3971f495a1..4e48bb6f63aa 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -194,7 +194,7 @@ impl From for ManagerOperation { pub fn serialize_unsigned_operation( branch: &BlockHash, - content: &ManagerOperationContent, + content: Vec, ) -> Result, BinError> { // Watermark comes from `src/lib_crypto/signature_v2.ml` // The watermark for a ManagerOperation is always `Generic_operation` @@ -205,7 +205,11 @@ pub fn serialize_unsigned_operation( let branch: [u8; 32] = branch.0.to_fixed_bytes(); tezos_data_encoding::enc::put_bytes(&branch, &mut serialized_unsigned_operation); - content.bin_write(&mut serialized_unsigned_operation)?; + tezos_data_encoding::enc::list(ManagerOperationContent::bin_write)( + &content, + &mut serialized_unsigned_operation, + ) + .expect("Failed to serialize the content"); Ok(serialized_unsigned_operation) } @@ -223,7 +227,8 @@ pub fn sign_operation( branch: &BlockHash, content: &ManagerOperationContent, ) -> Result { - let serialized_unsigned_operation = serialize_unsigned_operation(branch, content)?; + let serialized_unsigned_operation = + serialize_unsigned_operation(branch, vec![content.clone()])?; let signature = sk.sign(serialized_unsigned_operation)?; @@ -233,10 +238,11 @@ pub fn sign_operation( pub fn verify_signature( pk: &PublicKey, branch: &BlockHash, - operation: &ManagerOperationContent, + content: &ManagerOperationContent, signature: UnknownSignature, ) -> Result { - let serialized_unsigned_operation = serialize_unsigned_operation(branch, operation)?; + let serialized_unsigned_operation = + serialize_unsigned_operation(branch, vec![content.clone()])?; let signature = &signature.into(); -- GitLab From 8cd9a6c37ee11b68eb2dfefe78f890050664afdd Mon Sep 17 00:00:00 2001 From: Luciano Freitas Date: Mon, 4 Aug 2025 08:47:14 +0200 Subject: [PATCH 4/4] Tezlink/Kernel: sign batches --- etherlink/kernel_latest/kernel/src/block.rs | 6 ++++-- etherlink/kernel_latest/tezos/src/operation.rs | 10 ++++------ etherlink/kernel_latest/tezos_execution/src/lib.rs | 9 +++++---- .../kernel_latest/tezos_execution/src/validate.rs | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 23e55b990e89..b9e5611c64f8 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -634,6 +634,7 @@ mod tests { use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; use tezos_tezlink::block::TezBlock; use tezos_tezlink::operation::ManagerOperation; + use tezos_tezlink::operation::ManagerOperationContent; use tezos_tezlink::operation::Operation; use tezos_tezlink::operation::OperationContent; use tezos_tezlink::operation::RevealContent; @@ -685,7 +686,7 @@ mod tests { content: OperationContent, ) -> Operation { let branch = TezBlock::genesis_block_hash().into(); - let manager_op = ManagerOperation { + let manager_op: ManagerOperationContent = ManagerOperation { source: source.pkh, fee: fee.into(), counter: counter.into(), @@ -695,7 +696,8 @@ mod tests { } .into(); - let signature = sign_operation(&source.sk, &branch, &manager_op).unwrap(); + let signature = + sign_operation(&source.sk, &branch, vec![manager_op.clone()]).unwrap(); Operation { branch, diff --git a/etherlink/kernel_latest/tezos/src/operation.rs b/etherlink/kernel_latest/tezos/src/operation.rs index 4e48bb6f63aa..ad034b48a49d 100644 --- a/etherlink/kernel_latest/tezos/src/operation.rs +++ b/etherlink/kernel_latest/tezos/src/operation.rs @@ -225,10 +225,9 @@ pub enum SignatureErrors { pub fn sign_operation( sk: &SecretKeyEd25519, branch: &BlockHash, - content: &ManagerOperationContent, + content: Vec, ) -> Result { - let serialized_unsigned_operation = - serialize_unsigned_operation(branch, vec![content.clone()])?; + let serialized_unsigned_operation = serialize_unsigned_operation(branch, content)?; let signature = sk.sign(serialized_unsigned_operation)?; @@ -238,11 +237,10 @@ pub fn sign_operation( pub fn verify_signature( pk: &PublicKey, branch: &BlockHash, - content: &ManagerOperationContent, + content: Vec, signature: UnknownSignature, ) -> Result { - let serialized_unsigned_operation = - serialize_unsigned_operation(branch, vec![content.clone()])?; + let serialized_unsigned_operation = serialize_unsigned_operation(branch, content)?; let signature = &signature.into(); diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs index 847433cbd330..ae67be0ac8fc 100644 --- a/etherlink/kernel_latest/tezos_execution/src/lib.rs +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -586,8 +586,8 @@ mod tests { use tezos_tezlink::{ block::TezBlock, operation::{ - sign_operation, ManagerOperation, Operation, OperationContent, Parameter, - RevealContent, TransferContent, + sign_operation, ManagerOperation, ManagerOperationContent, Operation, + OperationContent, Parameter, RevealContent, TransferContent, }, operation_result::{ ApplyOperationError, Balance, BalanceTooLow, BalanceUpdate, ContentResult, @@ -668,7 +668,7 @@ mod tests { content: OperationContent, ) -> Operation { let branch = TezBlock::genesis_block_hash().into(); - let manager_op = ManagerOperation { + let manager_op: ManagerOperationContent = ManagerOperation { source: source.pkh, fee: fee.into(), counter: counter.into(), @@ -678,7 +678,8 @@ mod tests { } .into(); - let signature = sign_operation(&source.sk, &branch, &manager_op).unwrap(); + let signature = + sign_operation(&source.sk, &branch, vec![manager_op.clone()]).unwrap(); Operation { branch, diff --git a/etherlink/kernel_latest/tezos_execution/src/validate.rs b/etherlink/kernel_latest/tezos_execution/src/validate.rs index df5687d64289..53aa7950f533 100644 --- a/etherlink/kernel_latest/tezos_execution/src/validate.rs +++ b/etherlink/kernel_latest/tezos_execution/src/validate.rs @@ -168,7 +168,7 @@ pub fn validate_operation( Err(_) => return Err(ValidityError::FailedToFetchBalance), }; - match verify_signature(&pk, branch, &(content.clone().into()), signature.clone()) { + match verify_signature(&pk, branch, vec![content.clone().into()], signature.clone()) { Ok(true) => (), _ => return Err(ValidityError::InvalidSignature), } -- GitLab