use pchain_types::serialization::Serializable;
use pchain_types::{
blockchain::{Command, CommandReceipt, ExitStatus, Log, Receipt, Transaction},
cryptography::PublicAddress,
};
use pchain_world_state::{states::WorldState, storage::WorldStateStorage};
use std::ops::{Deref, DerefMut};
use crate::{
contract::SmartContractContext,
cost::CostChange,
execution::{execute, state::ExecutionState},
read_write_set::ReadWriteSet,
types::{BaseTx, DeferredCommand},
wasmer::cache::Cache,
BlockchainParams, TransitionError,
};
#[inline]
pub const fn cbi_version() -> u32 {
crate::contract::CBI_VERSION
}
pub struct Runtime {
sc_cache: Option<Cache>,
sc_memory_limit: Option<usize>,
}
impl Runtime {
pub fn new() -> Self {
Self {
sc_cache: None,
sc_memory_limit: None,
}
}
pub fn set_smart_contract_cache(mut self, sc_cache: Cache) -> Self {
self.sc_cache = Some(sc_cache);
self
}
pub fn set_smart_contract_memory_limit(mut self, memory_limit: usize) -> Self {
self.sc_memory_limit = Some(memory_limit);
self
}
pub fn transition<S: WorldStateStorage + Send + Sync + Clone + 'static>(
&self,
ws: WorldState<S>,
tx: Transaction,
bd: BlockchainParams,
) -> TransitionResult<S> {
let mut ctx = TransitionContext::new(ws);
if let Some(cache) = &self.sc_cache {
ctx.sc_context.cache = Some(cache.clone());
}
ctx.sc_context.memory_limit = self.sc_memory_limit;
let tx_size = tx.serialize().len();
let base_tx = BaseTx::from(&tx);
let commands = tx.commands;
let state = ExecutionState {
tx: base_tx,
tx_size,
commands_len: commands.len(),
ctx,
bd,
};
if commands.iter().any(|c| matches!(c, Command::NextEpoch)) {
execute::execute_next_epoch_command(state, commands)
} else {
execute::execute_commands(state, commands)
}
}
pub fn view<S: WorldStateStorage + Send + Sync + Clone + 'static>(
&self,
ws: WorldState<S>,
gas_limit: u64,
target: PublicAddress,
method: String,
arguments: Option<Vec<Vec<u8>>>,
) -> (CommandReceipt, Option<TransitionError>) {
let mut ctx = TransitionContext::new(ws);
if let Some(cache) = &self.sc_cache {
ctx.sc_context.cache = Some(cache.clone());
}
ctx.sc_context.memory_limit = self.sc_memory_limit;
let dummy_tx = BaseTx {
gas_limit,
..Default::default()
};
let dummy_bd = BlockchainParams::default();
let state = ExecutionState {
tx: dummy_tx,
bd: dummy_bd,
ctx,
tx_size: 0,
commands_len: 0,
};
execute::execute_view(state, target, method, arguments)
}
}
#[derive(Clone)]
pub struct TransitionResult<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub new_state: WorldState<S>,
pub receipt: Option<Receipt>,
pub error: Option<TransitionError>,
pub validator_changes: Option<ValidatorChanges>,
}
pub(crate) struct StateChangesResult<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub state: ExecutionState<S>,
pub error: Option<TransitionError>,
}
impl<S> StateChangesResult<S>
where
S: WorldStateStorage + Send + Sync + Clone + 'static,
{
pub(crate) fn new(
state: ExecutionState<S>,
transition_error: Option<TransitionError>,
) -> StateChangesResult<S> {
Self {
state,
error: transition_error,
}
}
pub(crate) fn finalize(self, command_receipts: Vec<CommandReceipt>) -> TransitionResult<S> {
let error = self.error;
let rw_set = self.state.ctx.rw_set;
let new_state = rw_set.commit_to_world_state();
TransitionResult {
new_state,
receipt: Some(command_receipts),
error,
validator_changes: None,
}
}
}
#[derive(Clone)]
pub struct ValidatorChanges {
pub new_validator_set: Vec<(PublicAddress, u64)>,
pub remove_validator_set: Vec<PublicAddress>,
}
#[derive(Clone)]
pub(crate) struct TransitionContext<S>
where
S: WorldStateStorage + Send + Sync + Clone,
{
pub rw_set: ReadWriteSet<S>,
pub sc_context: SmartContractContext,
pub commands: Vec<DeferredCommand>,
gas_used: u64,
pub receipt_write_gas: CostChange,
pub logs: Vec<Log>,
pub return_value: Option<Vec<u8>>,
}
impl<S> TransitionContext<S>
where
S: WorldStateStorage + Send + Sync + Clone,
{
pub fn new(ws: WorldState<S>) -> Self {
Self {
rw_set: ReadWriteSet::new(ws),
sc_context: SmartContractContext {
cache: None,
memory_limit: None,
},
receipt_write_gas: CostChange::default(),
logs: Vec::new(),
gas_used: 0,
return_value: None,
commands: Vec::new(),
}
}
pub fn gas_consumed(&self) -> u64 {
self.gas_used
}
pub fn set_gas_consumed(&mut self, gas_used: u64) {
self.gas_used = gas_used
}
pub fn total_gas_to_be_consumed(&self) -> u64 {
let chargeable_gas =
(self.rw_set.write_gas + self.receipt_write_gas + *self.rw_set.read_gas.borrow())
.values()
.0;
self.gas_consumed().saturating_add(chargeable_gas)
}
pub fn revert_changes(&mut self) {
self.rw_set.reads.borrow_mut().clear();
self.rw_set.writes.clear();
}
pub fn extract(&mut self, prev_gas_used: u64, exit_status: ExitStatus) -> CommandReceipt {
let ret = CommandReceipt {
exit_status,
gas_used: self.gas_used.saturating_sub(prev_gas_used),
return_values: self
.return_value
.clone()
.map_or(Vec::new(), std::convert::identity),
logs: self.logs.clone(),
};
*self.rw_set.read_gas.borrow_mut() = CostChange::default();
self.rw_set.write_gas = CostChange::default();
self.receipt_write_gas = CostChange::default();
self.logs.clear();
self.return_value = None;
self.commands.clear();
ret
}
pub fn pop_commands(&mut self) -> Option<Vec<DeferredCommand>> {
if self.commands.is_empty() {
return None;
}
let mut ret = Vec::new();
ret.append(&mut self.commands);
Some(ret)
}
}
impl<S> Deref for TransitionContext<S>
where
S: WorldStateStorage + Send + Sync + Clone,
{
type Target = ReadWriteSet<S>;
fn deref(&self) -> &Self::Target {
&self.rw_set
}
}
impl<S> DerefMut for TransitionContext<S>
where
S: WorldStateStorage + Send + Sync + Clone,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.rw_set
}
}