From de86a1a79ca0e26c8029b3ba32c4fbe4866f5d4f Mon Sep 17 00:00:00 2001 From: Voxelot Date: Fri, 4 Oct 2024 13:03:22 -0700 Subject: [PATCH 01/17] allow ldc in predicates, still need to deal with storage and checked txs --- fuel-asm/src/lib.rs | 2 +- fuel-vm/src/interpreter/blockchain.rs | 7 ++++++- fuel-vm/src/interpreter/executors/main.rs | 1 - fuel-vm/src/interpreter/executors/predicate.rs | 11 ----------- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/fuel-asm/src/lib.rs b/fuel-asm/src/lib.rs index 9c89930cc5..98de011f6a 100644 --- a/fuel-asm/src/lib.rs +++ b/fuel-asm/src/lib.rs @@ -701,7 +701,7 @@ impl Opcode { | K256 | S256 | NOOP | FLAG | ADDI | ANDI | DIVI | EXPI | MODI | MULI | MLDV | ORI | SLLI | SRLI | SUBI | XORI | JNEI | LB | LW | SB | SW | MCPI | MCLI | GM | MOVI | JNZI | JI | JMP | JNE | JMPF | JMPB | JNZF - | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF => true, + | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF | LDC => true, _ => false, } } diff --git a/fuel-vm/src/interpreter/blockchain.rs b/fuel-vm/src/interpreter/blockchain.rs index eed5e364d9..cb078b8273 100644 --- a/fuel-vm/src/interpreter/blockchain.rs +++ b/fuel-vm/src/interpreter/blockchain.rs @@ -104,7 +104,7 @@ where /// contract_code = contracts[contract_id] /// mem[$ssp, $rC] = contract_code[$rB, $rC] /// ``` - pub(crate) fn load_contract_code( + pub(crate) fn zload_contract_code( &mut self, id_addr: Word, offset: Word, @@ -599,6 +599,11 @@ where let sp = *self.sp; let region_start = ssp; + // only blobs are allowed in predicates + if self.context.is_predicate() { + return Err(PanicReason::ContractInstructionNotAllowed.into()) + } + if ssp != sp { return Err(PanicReason::ExpectedUnallocatedStack.into()) } diff --git a/fuel-vm/src/interpreter/executors/main.rs b/fuel-vm/src/interpreter/executors/main.rs index 63434c7a53..1321bfafc5 100644 --- a/fuel-vm/src/interpreter/executors/main.rs +++ b/fuel-vm/src/interpreter/executors/main.rs @@ -794,7 +794,6 @@ where impl Interpreter where M: Memory, - S: InterpreterStorage, Tx: ExecutableTransaction, Ecal: EcalHandler, diff --git a/fuel-vm/src/interpreter/executors/predicate.rs b/fuel-vm/src/interpreter/executors/predicate.rs index 1455a35619..3f754de135 100644 --- a/fuel-vm/src/interpreter/executors/predicate.rs +++ b/fuel-vm/src/interpreter/executors/predicate.rs @@ -30,18 +30,7 @@ where pub(crate) fn verify_predicate( &mut self, ) -> Result { - let range = self - .context - .predicate() - .expect("The predicate is not initialized") - .program() - .words(); - loop { - if range.end <= self.registers[RegId::PC] { - return Err(PanicReason::MemoryOverflow.into()) - } - match self.execute()? { ExecuteState::Return(r) => { if r == 1 { From 4225346fd56362e2e9d581348c5984a79a96a30b Mon Sep 17 00:00:00 2001 From: Voxelot Date: Fri, 4 Oct 2024 15:58:06 -0700 Subject: [PATCH 02/17] checkpoint, plumbing storage and refactoring generics --- fuel-vm/src/checked_transaction.rs | 200 ++++++++++----- fuel-vm/src/checked_transaction/builder.rs | 19 +- fuel-vm/src/error.rs | 4 +- fuel-vm/src/interpreter/blockchain.rs | 2 +- fuel-vm/src/interpreter/executors/main.rs | 50 +++- .../src/interpreter/executors/predicate.rs | 12 +- fuel-vm/src/lib.rs | 4 +- fuel-vm/src/storage/predicate.rs | 242 +++++++++++------- fuel-vm/src/util.rs | 94 +++++-- 9 files changed, 434 insertions(+), 193 deletions(-) diff --git a/fuel-vm/src/checked_transaction.rs b/fuel-vm/src/checked_transaction.rs index a77975b746..420f25f9a1 100644 --- a/fuel-vm/src/checked_transaction.rs +++ b/fuel-vm/src/checked_transaction.rs @@ -28,7 +28,7 @@ use core::{ fmt::Debug, future::Future, }; - +use fuel_storage::StorageRead; use fuel_tx::{ field::MaxFeeLimit, ConsensusParameters, @@ -49,6 +49,7 @@ use crate::{ }, pool::VmMemoryPool, prelude::*, + storage::BlobData, }; bitflags::bitflags! { @@ -232,9 +233,10 @@ where Checked: CheckPredicates, { fn default() -> Self { - Tx::default() - .into_checked(Default::default(), &ConsensusParameters::standard()) - .expect("default tx should produce a valid fully checked transaction") + todo!(); + // Tx::default() + // .into_checked(Default::default(), &ConsensusParameters::standard()) + //.expect("default tx should produce a valid fully checked transaction") } } @@ -293,36 +295,43 @@ pub trait IntoChecked: FormatValidityChecks + Sized { type Metadata: Sized; /// Returns transaction that passed all `Checks`. - fn into_checked( + fn into_checked( self, block_height: BlockHeight, consensus_params: &ConsensusParameters, + storage: S, ) -> Result, CheckError> where Checked: CheckPredicates, + S: StorageRead + Clone, + S::Error: Debug, { self.into_checked_reusable_memory( block_height, consensus_params, MemoryInstance::new(), + storage, ) } /// Returns transaction that passed all `Checks` accepting reusable memory /// to run predicates. - fn into_checked_reusable_memory( + fn into_checked_reusable_memory( self, block_height: BlockHeight, consensus_params: &ConsensusParameters, memory: impl Memory, + storage: S, ) -> Result, CheckError> where Checked: CheckPredicates, + S: StorageRead + Clone, + S::Error: Debug, { let check_predicate_params = consensus_params.into(); self.into_checked_basic(block_height, consensus_params)? .check_signatures(&consensus_params.chain_id())? - .check_predicates(&check_predicate_params, memory) + .check_predicates(&check_predicate_params, memory, storage) } /// Returns transaction that passed only `Checks::Basic`. @@ -392,36 +401,52 @@ impl From<&ConsensusParameters> for CheckPredicateParams { #[async_trait::async_trait] pub trait CheckPredicates: Sized { /// Performs predicates verification of the transaction. - fn check_predicates( + fn check_predicates( self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result; + storage: S, + ) -> Result + where + S: StorageRead + Clone, + S::Error: Debug; /// Performs predicates verification of the transaction in parallel. - async fn check_predicates_async( + async fn check_predicates_async( self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, - ) -> Result; + storage: S, + ) -> Result + where + S: StorageRead + Send + 'static + Clone, + S::Error: Debug; } /// Provides predicate estimation functionality for the transaction. #[async_trait::async_trait] pub trait EstimatePredicates: Sized { /// Estimates predicates of the transaction. - fn estimate_predicates( + fn estimate_predicates( &mut self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result<(), CheckError>; + storage: S, + ) -> Result<(), CheckError> + where + S: StorageRead + Clone, + S::Error: Debug; /// Estimates predicates of the transaction in parallel. - async fn estimate_predicates_async( + async fn estimate_predicates_async( &mut self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, - ) -> Result<(), CheckError>; + storage: S, + ) -> Result<(), CheckError> + where + S: StorageRead + Send + 'static + Clone, + S::Error: Debug; } /// Executes CPU-heavy tasks in parallel. @@ -449,28 +474,37 @@ where Tx: ExecutableTransaction + Send + Sync + 'static, ::Metadata: crate::interpreter::CheckedMetadata + Send + Sync, { - fn check_predicates( + fn check_predicates( mut self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result { + storage: S, + ) -> Result + where + S: StorageRead + Clone, + S::Error: Debug, + { if !self.checks_bitmask.contains(Checks::Predicates) { - Interpreter::check_predicates(&self, params, memory)?; + Interpreter::<&mut MemoryInstance, PredicateStorage, Tx>::check_predicates(&self, params, memory, storage)?; self.checks_bitmask.insert(Checks::Predicates); } Ok(self) } - async fn check_predicates_async( + async fn check_predicates_async( mut self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result where E: ParallelExecutor, + S: StorageRead + Clone + Send + 'static, + S::Error: Debug, { if !self.checks_bitmask.contains(Checks::Predicates) { - Interpreter::check_predicates_async::(&self, params, pool).await?; + Interpreter::check_predicates_async::(&self, params, pool, storage) + .await?; self.checks_bitmask.insert(Checks::Predicates); @@ -483,24 +517,32 @@ where #[async_trait::async_trait] impl EstimatePredicates for Tx { - fn estimate_predicates( + fn estimate_predicates( &mut self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result<(), CheckError> { - Interpreter::estimate_predicates(self, params, memory)?; + storage: S, + ) -> Result<(), CheckError> + where + S: StorageRead + Clone, + S::Error: Debug, + { + Interpreter::estimate_predicates(self, params, memory, storage)?; Ok(()) } - async fn estimate_predicates_async( + async fn estimate_predicates_async( &mut self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result<(), CheckError> where E: ParallelExecutor, + S: StorageRead + Clone + Send + 'static, + S::Error: Debug, { - Interpreter::estimate_predicates_async::(self, params, pool).await?; + Interpreter::estimate_predicates_async::(self, params, pool, storage).await?; Ok(()) } @@ -508,53 +550,87 @@ impl EstimatePredicates for T #[async_trait::async_trait] impl EstimatePredicates for Transaction { - fn estimate_predicates( + fn estimate_predicates( &mut self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result<(), CheckError> { + storage: S, + ) -> Result<(), CheckError> + where + S: StorageRead + Clone, + S::Error: Debug, + { match self { - Self::Script(tx) => tx.estimate_predicates(params, memory), - Self::Create(tx) => tx.estimate_predicates(params, memory), + Self::Script(tx) => tx.estimate_predicates(params, memory, storage), + Self::Create(tx) => tx.estimate_predicates(params, memory, storage), Self::Mint(_) => Ok(()), - Self::Upgrade(tx) => tx.estimate_predicates(params, memory), - Self::Upload(tx) => tx.estimate_predicates(params, memory), - Self::Blob(tx) => tx.estimate_predicates(params, memory), + Self::Upgrade(tx) => tx.estimate_predicates(params, memory, storage), + Self::Upload(tx) => tx.estimate_predicates(params, memory, storage), + Self::Blob(tx) => tx.estimate_predicates(params, memory, storage), } } - async fn estimate_predicates_async( + async fn estimate_predicates_async( &mut self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, - ) -> Result<(), CheckError> { + storage: S, + ) -> Result<(), CheckError> + where + S: StorageRead + Clone + Send + 'static, + S::Error: Debug, + { match self { - Self::Script(tx) => tx.estimate_predicates_async::(params, pool).await, - Self::Create(tx) => tx.estimate_predicates_async::(params, pool).await, + Self::Script(tx) => { + tx.estimate_predicates_async::(params, pool, storage) + .await + } + Self::Create(tx) => { + tx.estimate_predicates_async::(params, pool, storage) + .await + } Self::Mint(_) => Ok(()), - Self::Upgrade(tx) => tx.estimate_predicates_async::(params, pool).await, - Self::Upload(tx) => tx.estimate_predicates_async::(params, pool).await, - Self::Blob(tx) => tx.estimate_predicates_async::(params, pool).await, + Self::Upgrade(tx) => { + tx.estimate_predicates_async::(params, pool, storage) + .await + } + Self::Upload(tx) => { + tx.estimate_predicates_async::(params, pool, storage) + .await + } + Self::Blob(tx) => { + tx.estimate_predicates_async::(params, pool, storage) + .await + } } } } #[async_trait::async_trait] impl CheckPredicates for Checked { - fn check_predicates( + fn check_predicates( mut self, _params: &CheckPredicateParams, _memory: impl Memory, - ) -> Result { + _storage: S, + ) -> Result + where + S: StorageRead + Clone, + S::Error: Debug, + { self.checks_bitmask.insert(Checks::Predicates); Ok(self) } - async fn check_predicates_async( + async fn check_predicates_async( mut self, _params: &CheckPredicateParams, _pool: &impl VmMemoryPool, - ) -> Result { + _storage: S, + ) -> Result + where + S: StorageRead + Clone + Send + 'static, + { self.checks_bitmask.insert(Checks::Predicates); Ok(self) } @@ -562,73 +638,81 @@ impl CheckPredicates for Checked { #[async_trait::async_trait] impl CheckPredicates for Checked { - fn check_predicates( + fn check_predicates( self, params: &CheckPredicateParams, memory: impl Memory, - ) -> Result { + storage: S, + ) -> Result + where + S: StorageRead + Clone, + S::Error: Debug, + { let checked_transaction: CheckedTransaction = self.into(); let checked_transaction: CheckedTransaction = match checked_transaction { CheckedTransaction::Script(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } CheckedTransaction::Create(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } CheckedTransaction::Mint(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } CheckedTransaction::Upgrade(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } CheckedTransaction::Upload(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } CheckedTransaction::Blob(tx) => { - CheckPredicates::check_predicates(tx, params, memory)?.into() + CheckPredicates::check_predicates(tx, params, memory, storage)?.into() } }; Ok(checked_transaction.into()) } - async fn check_predicates_async( + async fn check_predicates_async( mut self, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result where E: ParallelExecutor, + S: StorageRead + Clone + Send + 'static, + S::Error: Debug, { let checked_transaction: CheckedTransaction = self.into(); let checked_transaction: CheckedTransaction = match checked_transaction { CheckedTransaction::Script(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } CheckedTransaction::Create(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } CheckedTransaction::Mint(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } CheckedTransaction::Upgrade(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } CheckedTransaction::Upload(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } CheckedTransaction::Blob(tx) => { - CheckPredicates::check_predicates_async::(tx, params, pool) + CheckPredicates::check_predicates_async::(tx, params, pool, storage) .await? .into() } diff --git a/fuel-vm/src/checked_transaction/builder.rs b/fuel-vm/src/checked_transaction/builder.rs index cfd9c9cc89..3aa7779871 100644 --- a/fuel-vm/src/checked_transaction/builder.rs +++ b/fuel-vm/src/checked_transaction/builder.rs @@ -7,6 +7,11 @@ use super::{ use crate::{ checked_transaction::CheckPredicates, prelude::*, + storage::BlobData, +}; +use fuel_storage::{ + StorageRead, + StorageSize, }; use fuel_tx::{ Finalizable, @@ -20,7 +25,11 @@ where Tx: IntoChecked, { /// Finalize the builder into a [`Checked`] of the correct type - fn finalize_checked(&self, height: BlockHeight) -> Checked; + fn finalize_checked( + &self, + height: BlockHeight, + storage: impl StorageSize + StorageRead + Clone, + ) -> Checked; /// Finalize the builder into a [`Checked`] of the correct type, with basic checks /// only @@ -32,9 +41,13 @@ where Self: Finalizable, Checked: CheckPredicates, { - fn finalize_checked(&self, height: BlockHeight) -> Checked { + fn finalize_checked( + &self, + height: BlockHeight, + storage: impl StorageSize + StorageRead + Clone, + ) -> Checked { self.finalize() - .into_checked(height, self.get_params()) + .into_checked(height, self.get_params(), storage) .expect("failed to check tx") } diff --git a/fuel-vm/src/error.rs b/fuel-vm/src/error.rs index 43c7c078ab..ae9a8364b7 100644 --- a/fuel-vm/src/error.rs +++ b/fuel-vm/src/error.rs @@ -285,10 +285,10 @@ pub enum PredicateVerificationFailed { Storage, } -impl From> +impl From>> for PredicateVerificationFailed { - fn from(error: InterpreterError) -> Self { + fn from(error: InterpreterError>) -> Self { match error { error if error.panic_reason() == Some(PanicReason::OutOfGas) => { PredicateVerificationFailed::OutOfGas diff --git a/fuel-vm/src/interpreter/blockchain.rs b/fuel-vm/src/interpreter/blockchain.rs index cb078b8273..8c9ecb4f3b 100644 --- a/fuel-vm/src/interpreter/blockchain.rs +++ b/fuel-vm/src/interpreter/blockchain.rs @@ -104,7 +104,7 @@ where /// contract_code = contracts[contract_id] /// mem[$ssp, $rC] = contract_code[$rB, $rC] /// ``` - pub(crate) fn zload_contract_code( + pub(crate) fn load_contract_code( &mut self, id_addr: Word, offset: Word, diff --git a/fuel-vm/src/interpreter/executors/main.rs b/fuel-vm/src/interpreter/executors/main.rs index 1321bfafc5..52495f5037 100644 --- a/fuel-vm/src/interpreter/executors/main.rs +++ b/fuel-vm/src/interpreter/executors/main.rs @@ -1,11 +1,6 @@ #[cfg(test)] mod tests; -use alloc::{ - vec, - vec::Vec, -}; - use crate::{ checked_transaction::{ Checked, @@ -44,6 +39,11 @@ use crate::{ PredicateStorage, }, }; +use alloc::{ + vec, + vec::Vec, +}; +use core::fmt::Debug; use crate::{ checked_transaction::{ @@ -62,6 +62,7 @@ use fuel_asm::PanicReason; use fuel_storage::{ StorageAsMut, StorageAsRef, + StorageRead, }; use fuel_tx::{ field::{ @@ -106,6 +107,7 @@ use fuel_types::{ BlobId, Word, }; +use crate::storage::predicate::PredicateBlobStorage; /// Predicates were checked succesfully #[derive(Debug, Clone, Copy)] @@ -139,9 +141,10 @@ enum PredicateAction { Estimating { available_gas: Word }, } -impl Interpreter<&mut MemoryInstance, PredicateStorage, Tx> +impl Interpreter<&mut MemoryInstance, PredicateStorage, Tx> where Tx: ExecutableTransaction, + S: PredicateBlobStorage, { /// Initialize the VM with the provided transaction and check all predicates defined /// in the inputs. @@ -152,12 +155,18 @@ where checked: &Checked, params: &CheckPredicateParams, mut memory: impl Memory, + storage: S, ) -> Result where ::Metadata: CheckedMetadata, { let tx = checked.transaction(); - Self::run_predicates(PredicateRunKind::Verifying(tx), params, memory.as_mut()) + Self::run_predicates( + PredicateRunKind::Verifying(tx), + params, + memory.as_mut(), + storage, + ) } /// Initialize the VM with the provided transaction and check all predicates defined @@ -169,17 +178,23 @@ where checked: &Checked, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result where Tx: Send + 'static, + S: Send + 'static, ::Metadata: CheckedMetadata, E: ParallelExecutor, { let tx = checked.transaction(); - let predicates_checked = - Self::run_predicate_async::(PredicateRunKind::Verifying(tx), params, pool) - .await?; + let predicates_checked = Self::run_predicate_async::( + PredicateRunKind::Verifying(tx), + params, + pool, + storage, + ) + .await?; Ok(predicates_checked) } @@ -194,11 +209,13 @@ where transaction: &mut Tx, params: &CheckPredicateParams, mut memory: impl Memory, + storage: S, ) -> Result { let predicates_checked = Self::run_predicates( PredicateRunKind::Estimating(transaction), params, memory.as_mut(), + storage, )?; Ok(predicates_checked) } @@ -213,15 +230,18 @@ where transaction: &mut Tx, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result where Tx: Send + 'static, + S: Send + 'static, E: ParallelExecutor, { let predicates_checked = Self::run_predicate_async::( PredicateRunKind::Estimating(transaction), params, pool, + storage, ) .await?; @@ -232,9 +252,11 @@ where kind: PredicateRunKind<'_, Tx>, params: &CheckPredicateParams, pool: &impl VmMemoryPool, + storage: S, ) -> Result where Tx: Send + 'static, + S: Send + 'static, E: ParallelExecutor, { let mut checks = vec![]; @@ -257,6 +279,7 @@ where let tx = kind.tx().clone(); let my_params = params.clone(); let mut memory = pool.get_new().await; + let my_storage = storage.clone(); let verify_task = E::create_task(move || { let (used_gas, result) = Interpreter::check_predicate( @@ -266,6 +289,7 @@ where predicate, my_params, memory.as_mut(), + my_storage, ); result.map(|_| (used_gas, index)) @@ -284,6 +308,7 @@ where kind: PredicateRunKind<'_, Tx>, params: &CheckPredicateParams, mut memory: impl Memory, + storage: S, ) -> Result { let mut checks = vec![]; @@ -295,6 +320,7 @@ where for index in 0..kind.tx().inputs().len() { let tx = kind.tx().clone(); + let storage = storage.clone(); if let Some(predicate) = RuntimePredicate::from_tx(&tx, params.tx_offset, index) @@ -312,6 +338,7 @@ where predicate, params.clone(), memory.as_mut(), + storage, ); available_gas = available_gas.saturating_sub(gas_used); let result = result.map(|_| (gas_used, index)); @@ -329,6 +356,7 @@ where predicate: RuntimePredicate, params: CheckPredicateParams, memory: &mut MemoryInstance, + storage: S, ) -> (Word, Result<(), PredicateVerificationFailed>) { match &tx.inputs()[index] { Input::CoinPredicate(CoinPredicate { @@ -358,7 +386,7 @@ where let mut vm = Interpreter::<_, _, _>::with_storage( memory, - PredicateStorage {}, + PredicateStorage::new(storage), interpreter_params, ); diff --git a/fuel-vm/src/interpreter/executors/predicate.rs b/fuel-vm/src/interpreter/executors/predicate.rs index 3f754de135..1bf5a95170 100644 --- a/fuel-vm/src/interpreter/executors/predicate.rs +++ b/fuel-vm/src/interpreter/executors/predicate.rs @@ -14,17 +14,19 @@ use crate::{ }, storage::PredicateStorage, }; +use core::fmt::Debug; -use fuel_asm::{ - PanicReason, - RegId, -}; +use crate::storage::BlobData; +use fuel_asm::PanicReason; +use fuel_storage::StorageRead; -impl Interpreter +impl Interpreter, Tx, Ecal> where M: Memory, Tx: ExecutableTransaction, Ecal: EcalHandler, + S: StorageRead, + S::Error: Debug, { /// Verify a predicate that has been initialized already pub(crate) fn verify_predicate( diff --git a/fuel-vm/src/lib.rs b/fuel-vm/src/lib.rs index 030f3214f4..446daa0fc7 100644 --- a/fuel-vm/src/lib.rs +++ b/fuel-vm/src/lib.rs @@ -18,7 +18,9 @@ pub extern crate alloc; extern crate core; #[cfg(feature = "std")] -extern crate libm as _; // Not needed with stdlib +extern crate libm as _; + +// Not needed with stdlib #[cfg(test)] use criterion as _; diff --git a/fuel-vm/src/storage/predicate.rs b/fuel-vm/src/storage/predicate.rs index 9f66c352cc..90f1ba5ac0 100644 --- a/fuel-vm/src/storage/predicate.rs +++ b/fuel-vm/src/storage/predicate.rs @@ -1,8 +1,3 @@ -use alloc::{ - borrow::Cow, - vec::Vec, -}; - use crate::{ prelude::{ InterpreterError, @@ -10,10 +5,16 @@ use crate::{ }, storage::InterpreterStorage, }; +use alloc::{ + borrow::Cow, + vec::Vec, +}; +use core::fmt::Debug; use fuel_asm::Word; use fuel_storage::{ Mappable, + StorageAsMut, StorageInspect, StorageMutate, StorageRead, @@ -40,11 +41,44 @@ use super::{ /// The storage implementations are expected to provide KV-like operations for contract /// operations. However, predicates, as defined in the protocol, cannot execute contract /// opcodes. This means its storage backend for predicate execution shouldn't provide any -/// functionality. -/// -/// TODO: blob storage should be implemented +/// functionality. Unless the storage access is limited to immutable data and read-only. #[derive(Debug, Default, Clone, Copy)] -pub struct PredicateStorage; +pub struct PredicateStorage { + storage: D, +} + +impl PredicateStorage { + pub fn new(storage: D) -> Self { + Self { storage } + } +} + +pub trait PredicateBlobStorage: StorageRead + Clone +where + Self::Error: Debug, +{ +} + +#[derive(Debug, Clone, Copy)] +pub enum PredicateStorageError { + /// Storage operation is unavailable in predicate context. + UnsupportedStorageOperation, + /// An storage error occurred + WrappedError(E), +} + +impl From> for InterpreterError> { + fn from(val: PredicateStorageError) -> Self { + let rt: RuntimeError = val.into(); + rt.into() + } +} + +impl From> for RuntimeError> { + fn from(val: PredicateStorageError) -> Self { + RuntimeError::Storage(val) + } +} /// Storage is unavailable in predicate context. #[derive(Debug, Clone, Copy)] @@ -63,71 +97,85 @@ impl From for RuntimeError { } } -impl StorageInspect for PredicateStorage { - type Error = StorageUnavailable; +impl StorageInspect for PredicateStorage +where + Type: Mappable, + D: StorageRead, +{ + type Error = PredicateStorageError; fn get( &self, _key: &Type::Key, - ) -> Result>, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result>, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } - fn contains_key(&self, _key: &Type::Key) -> Result { - Err(StorageUnavailable) + fn contains_key(&self, _key: &Type::Key) -> Result { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageMutate for PredicateStorage { +impl StorageMutate for PredicateStorage +where + Type: Mappable, + D: StorageRead, +{ fn replace( &mut self, _key: &Type::Key, _value: &Type::Value, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } fn take( &mut self, _key: &Type::Key, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageSize for PredicateStorage { - fn size_of_value( - &self, - _key: &ContractId, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) +impl StorageSize for PredicateStorage +where + D: StorageRead, +{ + fn size_of_value(&self, _key: &ContractId) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageRead for PredicateStorage { +impl StorageRead for PredicateStorage +where + D: StorageRead, +{ fn read( &self, _key: &::Key, _buf: &mut [u8], - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } fn read_alloc( &self, _key: &::Key, - ) -> Result>, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result>, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageWrite for PredicateStorage { +impl StorageWrite for PredicateStorage +where + D: StorageRead, +{ fn write_bytes( &mut self, _key: &::Key, _buf: &[u8], ) -> Result { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn replace_bytes( @@ -135,50 +183,59 @@ impl StorageWrite for PredicateStorage { _key: &::Key, _buf: &[u8], ) -> Result<(usize, Option>), Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn take_bytes( &mut self, _key: &::Key, ) -> Result>, Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageSize for PredicateStorage { +impl StorageSize for PredicateStorage +where + D: StorageRead, +{ fn size_of_value( &self, _key: &::Key, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageRead for PredicateStorage { +impl StorageRead for PredicateStorage +where + D: StorageRead, +{ fn read( &self, _key: &::Key, _buf: &mut [u8], - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } fn read_alloc( &self, _key: &::Key, - ) -> Result>, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result>, Self::Error> { + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageWrite for PredicateStorage { +impl StorageWrite for PredicateStorage +where + D: StorageRead, +{ fn write_bytes( &mut self, _key: &::Key, _buf: &[u8], ) -> Result { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn replace_bytes( @@ -186,50 +243,62 @@ impl StorageWrite for PredicateStorage { _key: &::Key, _buf: &[u8], ) -> Result<(usize, Option>), Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn take_bytes( &mut self, _key: &::Key, ) -> Result>, Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } } -impl StorageSize for PredicateStorage { +impl StorageSize for PredicateStorage +where + D: StorageRead, +{ fn size_of_value( &self, - _key: &::Key, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + key: &::Key, + ) -> Result, Self::Error> { + StorageSize::::size_of_value(&self.storage, key) + .map_err(|e| Self::Error::WrappedError(e)) } } -impl StorageRead for PredicateStorage { +impl StorageRead for PredicateStorage +where + D: StorageRead, +{ fn read( &self, - _key: &::Key, - _buf: &mut [u8], - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + key: &::Key, + buf: &mut [u8], + ) -> Result, Self::Error> { + StorageRead::::read(&self.storage, key, buf) + .map_err(|e| Self::Error::WrappedError(e)) } fn read_alloc( &self, - _key: &::Key, - ) -> Result>, StorageUnavailable> { - Err(StorageUnavailable) + key: &::Key, + ) -> Result>, Self::Error> { + StorageRead::::read_alloc(&self.storage, key) + .map_err(|e| Self::Error::WrappedError(e)) } } -impl StorageWrite for PredicateStorage { +impl StorageWrite for PredicateStorage +where + D: StorageRead, +{ fn write_bytes( &mut self, _key: &::Key, _buf: &[u8], ) -> Result { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn replace_bytes( @@ -237,47 +306,48 @@ impl StorageWrite for PredicateStorage { _key: &::Key, _buf: &[u8], ) -> Result<(usize, Option>), Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } fn take_bytes( &mut self, _key: &::Key, ) -> Result>, Self::Error> { - Err(StorageUnavailable) + Err(Self::Error::UnsupportedStorageOperation) } } -impl ContractsAssetsStorage for PredicateStorage {} +impl ContractsAssetsStorage for PredicateStorage where D: StorageRead {} -impl InterpreterStorage for PredicateStorage { - type DataError = StorageUnavailable; +impl InterpreterStorage for PredicateStorage +where + D: StorageRead, + D::Error: Debug, +{ + type DataError = PredicateStorageError; - fn block_height(&self) -> Result { - Err(StorageUnavailable) + fn block_height(&self) -> Result { + Err(Self::DataError::UnsupportedStorageOperation) } fn consensus_parameters_version(&self) -> Result { - Err(StorageUnavailable) + Err(Self::DataError::UnsupportedStorageOperation) } fn state_transition_version(&self) -> Result { - Err(StorageUnavailable) + Err(Self::DataError::UnsupportedStorageOperation) } - fn timestamp(&self, _height: BlockHeight) -> Result { - Err(StorageUnavailable) + fn timestamp(&self, _height: BlockHeight) -> Result { + Err(Self::DataError::UnsupportedStorageOperation) } - fn block_hash( - &self, - _block_height: BlockHeight, - ) -> Result { - Err(StorageUnavailable) + fn block_hash(&self, _block_height: BlockHeight) -> Result { + Err(Self::DataError::UnsupportedStorageOperation) } - fn coinbase(&self) -> Result { - Err(StorageUnavailable) + fn coinbase(&self) -> Result { + Err(Self::DataError::UnsupportedStorageOperation) } fn set_consensus_parameters( @@ -285,7 +355,7 @@ impl InterpreterStorage for PredicateStorage { _version: u32, _consensus_parameters: &ConsensusParameters, ) -> Result, Self::DataError> { - Err(StorageUnavailable) + Err(Self::DataError::UnsupportedStorageOperation) } fn set_state_transition_bytecode( @@ -293,7 +363,7 @@ impl InterpreterStorage for PredicateStorage { _version: u32, _hash: &Bytes32, ) -> Result, Self::DataError> { - Err(StorageUnavailable) + Err(Self::DataError::UnsupportedStorageOperation) } fn contract_state_range( @@ -301,8 +371,8 @@ impl InterpreterStorage for PredicateStorage { _id: &ContractId, _start_key: &Bytes32, _range: usize, - ) -> Result>>, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result>>, Self::DataError> { + Err(Self::DataError::UnsupportedStorageOperation) } fn contract_state_insert_range<'a, I>( @@ -314,7 +384,7 @@ impl InterpreterStorage for PredicateStorage { where I: Iterator, { - Err(StorageUnavailable) + Err(Self::DataError::UnsupportedStorageOperation) } fn contract_state_remove_range( @@ -322,7 +392,7 @@ impl InterpreterStorage for PredicateStorage { _contract: &ContractId, _start_key: &Bytes32, _range: usize, - ) -> Result, StorageUnavailable> { - Err(StorageUnavailable) + ) -> Result, Self::DataError> { + Err(Self::DataError::UnsupportedStorageOperation) } } diff --git a/fuel-vm/src/util.rs b/fuel-vm/src/util.rs index b3c6d96ed5..f33279cb65 100644 --- a/fuel-vm/src/util.rs +++ b/fuel-vm/src/util.rs @@ -119,6 +119,7 @@ pub mod test_helpers { Backtrace, Call, }, + storage::BlobData, }; use fuel_asm::{ op, @@ -127,6 +128,7 @@ pub mod test_helpers { PanicReason, RegId, }; + use fuel_storage::StorageRead; use fuel_tx::{ field::{ Outputs, @@ -355,7 +357,10 @@ pub mod test_helpers { self } - pub fn build(&mut self) -> Checked