use std::sync::Arc;
use borsh::BorshSerialize;
use bytes::Bytes;
use casper_storage::{
global_state::{error::Error as GlobalStateError, GlobalStateReader},
tracking_copy::TrackingCopyCache,
AddressGenerator, TrackingCopy,
};
use casper_types::{
account::AccountHash, contract_messages::Messages, execution::Effects, BlockHash, BlockTime,
Digest, HashAddr, Key, TransactionHash,
};
use parking_lot::RwLock;
use thiserror::Error;
use crate::{CallError, GasUsage, InternalHostError, WasmPreparationError};
pub struct ExecuteRequest {
pub initiator: AccountHash,
pub caller_key: Key,
pub gas_limit: u64,
pub execution_kind: ExecutionKind,
pub input: Bytes,
pub transferred_value: u64,
pub transaction_hash: TransactionHash,
pub address_generator: Arc<RwLock<AddressGenerator>>,
pub chain_name: Arc<str>,
pub block_time: BlockTime,
pub state_hash: Digest,
pub parent_block_hash: BlockHash,
pub block_height: u64,
}
#[derive(Default)]
pub struct ExecuteRequestBuilder {
initiator: Option<AccountHash>,
caller_key: Option<Key>,
gas_limit: Option<u64>,
target: Option<ExecutionKind>,
input: Option<Bytes>,
value: Option<u64>,
transaction_hash: Option<TransactionHash>,
address_generator: Option<Arc<RwLock<AddressGenerator>>>,
chain_name: Option<Arc<str>>,
block_time: Option<BlockTime>,
state_hash: Option<Digest>,
parent_block_hash: Option<BlockHash>,
block_height: Option<u64>,
}
impl ExecuteRequestBuilder {
#[must_use]
pub fn with_initiator(mut self, initiator: AccountHash) -> Self {
self.initiator = Some(initiator);
self
}
#[must_use]
pub fn with_caller_key(mut self, caller_key: Key) -> Self {
self.caller_key = Some(caller_key);
self
}
#[must_use]
pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
self.gas_limit = Some(gas_limit);
self
}
#[must_use]
pub fn with_target(mut self, target: ExecutionKind) -> Self {
self.target = Some(target);
self
}
#[must_use]
pub fn with_input(mut self, input: Bytes) -> Self {
self.input = Some(input);
self
}
#[must_use]
pub fn with_serialized_input<T: BorshSerialize>(self, input: T) -> Self {
let input = borsh::to_vec(&input)
.map(Bytes::from)
.expect("should serialize input");
self.with_input(input)
}
#[must_use]
pub fn with_transferred_value(mut self, value: u64) -> Self {
self.value = Some(value);
self
}
#[must_use]
pub fn with_transaction_hash(mut self, transaction_hash: TransactionHash) -> Self {
self.transaction_hash = Some(transaction_hash);
self
}
#[must_use]
pub fn with_address_generator(mut self, address_generator: AddressGenerator) -> Self {
self.address_generator = Some(Arc::new(RwLock::new(address_generator)));
self
}
#[must_use]
pub fn with_shared_address_generator(
mut self,
address_generator: Arc<RwLock<AddressGenerator>>,
) -> Self {
self.address_generator = Some(address_generator);
self
}
#[must_use]
pub fn with_chain_name<T: Into<Arc<str>>>(mut self, chain_name: T) -> Self {
self.chain_name = Some(chain_name.into());
self
}
#[must_use]
pub fn with_block_time(mut self, block_time: BlockTime) -> Self {
self.block_time = Some(block_time);
self
}
#[must_use]
pub fn with_state_hash(mut self, state_hash: Digest) -> Self {
self.state_hash = Some(state_hash);
self
}
#[must_use]
pub fn with_parent_block_hash(mut self, parent_block_hash: BlockHash) -> Self {
self.parent_block_hash = Some(parent_block_hash);
self
}
#[must_use]
pub fn with_block_height(mut self, block_height: u64) -> Self {
self.block_height = Some(block_height);
self
}
pub fn build(self) -> Result<ExecuteRequest, &'static str> {
let initiator = self.initiator.ok_or("Initiator is not set")?;
let caller_key = self.caller_key.ok_or("Caller is not set")?;
let gas_limit = self.gas_limit.ok_or("Gas limit is not set")?;
let execution_kind = self.target.ok_or("Target is not set")?;
let input = self.input.ok_or("Input is not set")?;
let transferred_value = self.value.ok_or("Value is not set")?;
let transaction_hash = self.transaction_hash.ok_or("Transaction hash is not set")?;
let address_generator = self
.address_generator
.ok_or("Address generator is not set")?;
let chain_name = self.chain_name.ok_or("Chain name is not set")?;
let block_time = self.block_time.ok_or("Block time is not set")?;
let state_hash = self.state_hash.ok_or("State hash is not set")?;
let parent_block_hash = self
.parent_block_hash
.ok_or("Parent block hash is not set")?;
let block_height = self.block_height.ok_or("Block height is not set")?;
Ok(ExecuteRequest {
initiator,
caller_key,
gas_limit,
execution_kind,
input,
transferred_value,
transaction_hash,
address_generator,
chain_name,
block_time,
state_hash,
parent_block_hash,
block_height,
})
}
}
#[derive(Debug)]
pub struct ExecuteResult {
pub host_error: Option<CallError>,
pub output: Option<Bytes>,
pub gas_usage: GasUsage,
pub effects: Effects,
pub cache: TrackingCopyCache,
pub messages: Messages,
}
impl ExecuteResult {
pub fn effects(&self) -> &Effects {
&self.effects
}
pub fn into_effects(self) -> Effects {
self.effects
}
pub fn host_error(&self) -> Option<&CallError> {
self.host_error.as_ref()
}
pub fn output(&self) -> Option<&Bytes> {
self.output.as_ref()
}
pub fn gas_usage(&self) -> &GasUsage {
&self.gas_usage
}
}
#[derive(Debug)]
pub struct ExecuteWithProviderResult {
pub host_error: Option<CallError>,
output: Option<Bytes>,
gas_usage: GasUsage,
effects: Effects,
post_state_hash: Digest,
messages: Messages,
}
impl ExecuteWithProviderResult {
#[must_use]
pub fn new(
host_error: Option<CallError>,
output: Option<Bytes>,
gas_usage: GasUsage,
effects: Effects,
post_state_hash: Digest,
messages: Messages,
) -> Self {
Self {
host_error,
output,
gas_usage,
effects,
post_state_hash,
messages,
}
}
pub fn output(&self) -> Option<&Bytes> {
self.output.as_ref()
}
pub fn gas_usage(&self) -> &GasUsage {
&self.gas_usage
}
pub fn effects(&self) -> &Effects {
&self.effects
}
#[must_use]
pub fn post_state_hash(&self) -> Digest {
self.post_state_hash
}
pub fn messages(&self) -> &Messages {
&self.messages
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionKind {
SessionBytes(Bytes),
Stored {
address: HashAddr,
entry_point: String,
},
}
#[derive(Debug, Error)]
pub enum ExecuteError {
#[error("Wasm error error: {0}")]
WasmPreparation(#[from] WasmPreparationError),
#[error("Internal host error: {0}")]
InternalHost(#[from] InternalHostError),
#[error("Code not found")]
CodeNotFound(HashAddr),
}
#[derive(Debug, Error)]
pub enum ExecuteWithProviderError {
#[error("Global state error: {0}")]
GlobalState(#[from] GlobalStateError),
#[error(transparent)]
Execute(#[from] ExecuteError),
}
pub trait Executor: Clone + Send {
fn execute<R: GlobalStateReader + 'static>(
&self,
tracking_copy: TrackingCopy<R>,
execute_request: ExecuteRequest,
) -> Result<ExecuteResult, ExecuteError>;
}