use crate::{
backtrace::Backtrace,
checked_transaction::{
Checked,
IntoChecked,
Ready,
},
error::InterpreterError,
interpreter::{
CheckedMetadata,
EcalHandler,
ExecutableTransaction,
Interpreter,
InterpreterParams,
Memory,
NotSupportedEcal,
},
state::{
ProgramState,
StateTransition,
StateTransitionRef,
},
storage::InterpreterStorage,
};
use fuel_tx::{
Blob,
Create,
FeeParameters,
GasCosts,
Receipt,
Script,
Upgrade,
Upload,
};
#[cfg(any(test, feature = "test-helpers"))]
use crate::interpreter::MemoryInstance;
#[derive(Debug)]
pub struct Transactor<M, S, Tx, Ecal = NotSupportedEcal>
where
S: InterpreterStorage,
{
interpreter: Interpreter<M, S, Tx, Ecal>,
program_state: Option<ProgramState>,
error: Option<InterpreterError<S::DataError>>,
}
impl<M, S, Tx, Ecal> Transactor<M, S, Tx, Ecal>
where
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler + Default,
{
pub fn new(memory: M, storage: S, interpreter_params: InterpreterParams) -> Self {
Self {
interpreter: Interpreter::<M, S, Tx, Ecal>::with_storage(
memory,
storage,
interpreter_params,
),
program_state: None,
error: None,
}
}
}
impl<M, S, Tx, Ecal> Transactor<M, S, Tx, Ecal>
where
S: InterpreterStorage,
Tx: ExecutableTransaction,
Ecal: EcalHandler,
{
pub fn state_transition(&self) -> Option<StateTransitionRef<'_, Tx>> {
self.program_state.map(|state| {
StateTransitionRef::new(
state,
self.interpreter.transaction(),
self.interpreter.receipts(),
)
})
}
pub fn to_owned_state_transition(&self) -> Option<StateTransition<Tx>> {
self.program_state.map(|state| {
StateTransition::new(
state,
self.interpreter.transaction().clone(),
self.interpreter.receipts().to_vec(),
)
})
}
pub const fn error(&self) -> Option<&InterpreterError<S::DataError>> {
self.error.as_ref()
}
pub const fn is_success(&self) -> bool {
!self.is_reverted()
}
pub const fn is_reverted(&self) -> bool {
self.error.is_some()
|| matches!(self.program_state, Some(ProgramState::Revert(_)))
}
pub fn result(
&self,
) -> Result<StateTransitionRef<'_, Tx>, &InterpreterError<S::DataError>> {
let state = self.state_transition();
let error = self.error.as_ref();
match (state, error) {
(Some(s), None) => Ok(s),
(None, Some(e)) => Err(e),
_ => Err(&InterpreterError::NoTransactionInitialized),
}
}
pub fn interpreter(&self) -> &Interpreter<M, S, Tx, Ecal> {
&self.interpreter
}
pub fn gas_costs(&self) -> &GasCosts {
self.interpreter.gas_costs()
}
pub fn fee_params(&self) -> &FeeParameters {
self.interpreter.fee_params()
}
#[cfg(feature = "test-helpers")]
pub fn set_gas_price(&mut self, gas_price: u64) {
self.interpreter.set_gas_price(gas_price);
}
pub fn tx_offset(&self) -> usize {
self.interpreter.tx_offset()
}
}
impl<M, S, Ecal> Transactor<M, S, Script, Ecal>
where
M: Memory,
S: InterpreterStorage,
{
pub fn receipts(&self) -> Option<&[Receipt]> {
self.program_state
.is_some()
.then(|| self.interpreter.receipts())
}
pub fn backtrace(&self) -> Option<Backtrace> {
self.receipts()
.and_then(|r| r.iter().find_map(Receipt::result))
.copied()
.map(|result| Backtrace::from_vm_error(&self.interpreter, result))
}
}
impl<M, S, Tx, Ecal> Transactor<M, S, Tx, Ecal>
where
S: InterpreterStorage,
{
pub fn deploy(
&mut self,
checked: Checked<Create>,
) -> Result<Create, InterpreterError<S::DataError>> {
let gas_price = self.interpreter.gas_price();
let gas_costs = self.interpreter.gas_costs();
let fee_params = self.interpreter.fee_params();
let ready = checked
.into_ready(gas_price, gas_costs, fee_params)
.map_err(InterpreterError::CheckError)?;
self.deploy_ready_tx(ready)
}
pub fn deploy_ready_tx(
&mut self,
ready_tx: Ready<Create>,
) -> Result<Create, InterpreterError<S::DataError>> {
self.interpreter.deploy(ready_tx)
}
pub fn upgrade(
&mut self,
checked: Checked<Upgrade>,
) -> Result<Upgrade, InterpreterError<S::DataError>> {
let gas_price = self.interpreter.gas_price();
let gas_costs = self.interpreter.gas_costs();
let fee_params = self.interpreter.fee_params();
let ready = checked
.into_ready(gas_price, gas_costs, fee_params)
.map_err(InterpreterError::CheckError)?;
self.execute_ready_upgrade_tx(ready)
}
pub fn execute_ready_upgrade_tx(
&mut self,
ready_tx: Ready<Upgrade>,
) -> Result<Upgrade, InterpreterError<S::DataError>> {
self.interpreter.upgrade(ready_tx)
}
pub fn upload(
&mut self,
checked: Checked<Upload>,
) -> Result<Upload, InterpreterError<S::DataError>> {
let gas_price = self.interpreter.gas_price();
let gas_costs = self.interpreter.gas_costs();
let fee_params = self.interpreter.fee_params();
let ready = checked
.into_ready(gas_price, gas_costs, fee_params)
.map_err(InterpreterError::CheckError)?;
self.execute_ready_upload_tx(ready)
}
pub fn execute_ready_upload_tx(
&mut self,
ready_tx: Ready<Upload>,
) -> Result<Upload, InterpreterError<S::DataError>> {
self.interpreter.upload(ready_tx)
}
pub fn blob(
&mut self,
checked: Checked<Blob>,
) -> Result<Blob, InterpreterError<S::DataError>> {
let gas_price = self.interpreter.gas_price();
let gas_costs = self.interpreter.gas_costs();
let fee_params = self.interpreter.fee_params();
let ready = checked
.into_ready(gas_price, gas_costs, fee_params)
.map_err(InterpreterError::CheckError)?;
self.execute_ready_blob_tx(ready)
}
pub fn execute_ready_blob_tx(
&mut self,
ready_tx: Ready<Blob>,
) -> Result<Blob, InterpreterError<S::DataError>> {
self.interpreter.blob(ready_tx)
}
}
impl<M, S, Tx, Ecal> Transactor<M, S, Tx, Ecal>
where
M: Memory,
S: InterpreterStorage,
Tx: ExecutableTransaction,
<Tx as IntoChecked>::Metadata: CheckedMetadata,
Ecal: EcalHandler,
{
pub fn transact(&mut self, tx: Checked<Tx>) -> &mut Self {
let gas_price = self.interpreter.gas_price();
let gas_costs = self.interpreter.gas_costs();
let fee_params = self.interpreter.fee_params();
match tx
.into_ready(gas_price, gas_costs, fee_params)
.map_err(InterpreterError::CheckError)
{
Ok(ready_tx) => self.transact_ready_tx(ready_tx),
Err(e) => self.handle_error(e),
}
}
pub fn transact_ready_tx(&mut self, ready_tx: Ready<Tx>) -> &mut Self {
match self.interpreter.transact(ready_tx) {
Ok(s) => {
self.program_state.replace(s.into());
self.error.take();
self
}
Err(e) => self.handle_error(e),
}
}
fn handle_error(&mut self, error: InterpreterError<S::DataError>) -> &mut Self {
self.program_state.take();
self.error.replace(error);
self
}
}
impl<M, S, Tx, Ecal> From<Interpreter<M, S, Tx, Ecal>> for Transactor<M, S, Tx, Ecal>
where
Tx: ExecutableTransaction,
S: InterpreterStorage,
{
fn from(interpreter: Interpreter<M, S, Tx, Ecal>) -> Self {
let program_state = None;
let error = None;
Self {
interpreter,
program_state,
error,
}
}
}
impl<M, S, Tx, Ecal> From<Transactor<M, S, Tx, Ecal>> for Interpreter<M, S, Tx, Ecal>
where
Tx: ExecutableTransaction,
S: InterpreterStorage,
{
fn from(transactor: Transactor<M, S, Tx, Ecal>) -> Self {
transactor.interpreter
}
}
impl<M, S, Tx, Ecal> AsRef<Interpreter<M, S, Tx, Ecal>> for Transactor<M, S, Tx, Ecal>
where
Tx: ExecutableTransaction,
S: InterpreterStorage,
Ecal: EcalHandler,
{
fn as_ref(&self) -> &Interpreter<M, S, Tx, Ecal> {
&self.interpreter
}
}
impl<M, S, Tx, Ecal> AsRef<S> for Transactor<M, S, Tx, Ecal>
where
Tx: ExecutableTransaction,
S: InterpreterStorage,
{
fn as_ref(&self) -> &S {
self.interpreter.as_ref()
}
}
impl<M, S, Tx, Ecal> AsMut<S> for Transactor<M, S, Tx, Ecal>
where
Tx: ExecutableTransaction,
S: InterpreterStorage,
{
fn as_mut(&mut self) -> &mut S {
self.interpreter.as_mut()
}
}
#[cfg(feature = "test-helpers")]
impl<S, Tx, Ecal> Default for Transactor<MemoryInstance, S, Tx, Ecal>
where
S: InterpreterStorage + Default,
Tx: ExecutableTransaction,
Ecal: EcalHandler + Default,
{
fn default() -> Self {
Self::new(
MemoryInstance::new(),
S::default(),
InterpreterParams::default(),
)
}
}