use std::sync::Arc;
use bytes::Bytes;
use casper_executor_wasm::{
install::{
InstallContractError, InstallContractRequest, InstallContractRequestBuilder,
InstallContractResult,
},
ExecutorV2,
};
use casper_executor_wasm_interface::{
executor::{
ExecuteRequest, ExecuteRequestBuilder, ExecuteWithProviderError, ExecuteWithProviderResult,
ExecutionKind,
},
GasUsage,
};
use casper_storage::{
global_state::state::{CommitProvider, StateProvider},
AddressGeneratorBuilder,
};
use casper_types::{
execution::Effects, BlockHash, Digest, Gas, Key, TransactionEntryPoint,
TransactionInvocationTarget, TransactionRuntimeParams, TransactionTarget, U512,
};
use thiserror::Error;
use tracing::info;
use super::MetaTransaction;
pub(crate) enum WasmV2Request {
Install(InstallContractRequest),
Execute(ExecuteRequest),
}
pub(crate) enum WasmV2Result {
Install(InstallContractResult),
Execute(ExecuteWithProviderResult),
}
impl WasmV2Result {
pub(crate) fn gas_usage(&self) -> &GasUsage {
match self {
WasmV2Result::Install(result) => result.gas_usage(),
WasmV2Result::Execute(result) => result.gas_usage(),
}
}
pub(crate) fn effects(&self) -> &Effects {
match self {
WasmV2Result::Install(result) => result.effects(),
WasmV2Result::Execute(result) => result.effects(),
}
}
pub(crate) fn post_state_hash(&self) -> Digest {
match self {
WasmV2Result::Install(result) => result.post_state_hash(),
WasmV2Result::Execute(result) => result.post_state_hash(),
}
}
}
#[derive(Error, Debug)]
pub(crate) enum WasmV2Error {
#[error(transparent)]
Install(InstallContractError),
#[error(transparent)]
Execute(ExecuteWithProviderError),
}
#[derive(Clone, Eq, PartialEq, Error, Debug)]
pub(crate) enum InvalidRequest {
#[error("Expected bytes arguments")]
ExpectedBytesArguments,
#[error("Expected target")]
ExpectedTarget,
#[error("Invalid gas limit: {0}")]
InvalidGasLimit(U512),
#[error("Expected transferred value")]
ExpectedTransferredValue,
#[error("Expected V2 runtime")]
ExpectedV2Runtime,
}
impl WasmV2Request {
pub(crate) fn new(
gas_limit: Gas,
network_name: impl Into<Arc<str>>,
state_root_hash: Digest,
parent_block_hash: BlockHash,
block_height: u64,
transaction: &MetaTransaction,
) -> Result<Self, InvalidRequest> {
let transaction_hash = transaction.hash();
let initiator_addr = transaction.initiator_addr();
let gas_limit: u64 = gas_limit
.value()
.try_into()
.map_err(|_| InvalidRequest::InvalidGasLimit(gas_limit.value()))?;
let address_generator = AddressGeneratorBuilder::default()
.seed_with(transaction_hash.as_ref())
.build();
let session_args = transaction.session_args();
let input_data = session_args
.as_bytesrepr()
.ok_or(InvalidRequest::ExpectedBytesArguments)?;
let value = transaction
.transferred_value()
.ok_or(InvalidRequest::ExpectedTransferredValue)?;
enum Target {
Install {
module_bytes: Bytes,
entry_point: String,
transferred_value: u64,
seed: Option<[u8; 32]>,
},
Session {
module_bytes: Bytes,
},
Stored {
id: TransactionInvocationTarget,
entry_point: String,
},
}
let transaction_target = transaction.target().ok_or(InvalidRequest::ExpectedTarget)?;
let target = match transaction_target {
TransactionTarget::Native => todo!(), TransactionTarget::Stored { id, runtime: _ } => match transaction.entry_point() {
TransactionEntryPoint::Custom(entry_point) => Target::Stored {
id: id.clone(),
entry_point: entry_point.clone(),
},
_ => todo!(),
},
TransactionTarget::Session {
module_bytes: _,
runtime: TransactionRuntimeParams::VmCasperV1,
is_install_upgrade: _, } => {
return Err(InvalidRequest::ExpectedV2Runtime);
}
TransactionTarget::Session {
module_bytes,
runtime:
TransactionRuntimeParams::VmCasperV2 {
transferred_value,
seed,
},
is_install_upgrade: _, } => match transaction.entry_point() {
TransactionEntryPoint::Call => Target::Session {
module_bytes: module_bytes.clone().take_inner().into(),
},
TransactionEntryPoint::Custom(entry_point) => Target::Install {
module_bytes: module_bytes.clone().take_inner().into(),
entry_point: entry_point.to_string(),
transferred_value,
seed,
},
_ => todo!(),
},
};
info!(%transaction_hash, "executing v1 contract");
match target {
Target::Install {
module_bytes,
entry_point,
transferred_value,
seed,
} => {
let mut builder = InstallContractRequestBuilder::default();
let entry_point = (!entry_point.is_empty()).then_some(entry_point);
match entry_point {
Some(entry_point) => {
builder = builder
.with_entry_point(entry_point.clone())
.with_input(input_data.clone().take_inner().into());
}
None => {
assert!(input_data.is_empty());
}
}
if let Some(seed) = seed {
builder = builder.with_seed(seed);
}
debug_assert_eq!(transferred_value, value);
let install_request = builder
.with_initiator(initiator_addr.account_hash())
.with_gas_limit(gas_limit)
.with_transaction_hash(transaction_hash)
.with_wasm_bytes(module_bytes)
.with_address_generator(address_generator)
.with_transferred_value(value)
.with_chain_name(network_name)
.with_block_time(transaction.timestamp().into())
.with_state_hash(state_root_hash)
.with_parent_block_hash(parent_block_hash)
.with_block_height(block_height)
.build()
.expect("should build");
Ok(Self::Install(install_request))
}
Target::Session { .. } | Target::Stored { .. } => {
let mut builder = ExecuteRequestBuilder::default();
let initiator_account_hash = &initiator_addr.account_hash();
let initiator_key = Key::Account(*initiator_account_hash);
builder = builder
.with_address_generator(address_generator)
.with_gas_limit(gas_limit)
.with_transaction_hash(transaction_hash)
.with_initiator(*initiator_account_hash)
.with_caller_key(initiator_key)
.with_chain_name(network_name)
.with_transferred_value(value)
.with_block_time(transaction.timestamp().into())
.with_input(input_data.clone().take_inner().into())
.with_state_hash(state_root_hash)
.with_parent_block_hash(parent_block_hash)
.with_block_height(block_height);
let execution_kind = match target {
Target::Session { module_bytes } => ExecutionKind::SessionBytes(module_bytes),
Target::Stored {
id: TransactionInvocationTarget::ByHash(smart_contract_addr),
entry_point,
} => ExecutionKind::Stored {
address: smart_contract_addr,
entry_point: entry_point.clone(),
},
Target::Stored { id, entry_point } => {
todo!("Unsupported target {entry_point} {id:?}")
}
Target::Install { .. } => unreachable!(),
};
builder = builder.with_target(execution_kind);
let execute_request = builder.build().expect("should build");
Ok(Self::Execute(execute_request))
}
}
}
pub(crate) fn execute<P>(
self,
engine: &ExecutorV2,
state_root_hash: Digest,
state_provider: &P,
) -> Result<WasmV2Result, WasmV2Error>
where
P: StateProvider + CommitProvider,
<P as StateProvider>::Reader: 'static,
{
match self {
WasmV2Request::Install(install_request) => {
match engine.install_contract(state_root_hash, state_provider, install_request) {
Ok(result) => Ok(WasmV2Result::Install(result)),
Err(error) => Err(WasmV2Error::Install(error)),
}
}
WasmV2Request::Execute(execute_request) => {
match engine.execute_with_provider(state_root_hash, state_provider, execute_request)
{
Ok(result) => Ok(WasmV2Result::Execute(result)),
Err(error) => Err(WasmV2Error::Execute(error)),
}
}
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn smoke_test() {}
}