use crate::ScriptError;
use ckb_error::Error;
use ckb_types::{
core::{Cycle, ScriptHashType},
packed::Script,
};
use ckb_vm::{
machine::{VERSION0, VERSION1},
snapshot::{make_snapshot, Snapshot},
Error as VMInternalError, SupportMachine, ISA_B, ISA_IMC, ISA_MOP,
};
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg(has_asm)]
use ckb_vm::machine::asm::{AsmCoreMachine, AsmMachine};
#[cfg(not(has_asm))]
use ckb_vm::{DefaultCoreMachine, SparseMemory, TraceMachine, WXorXMemory};
pub type VmIsa = u8;
pub type VmVersion = u32;
#[cfg(has_asm)]
pub(crate) type CoreMachineType = AsmCoreMachine;
#[cfg(not(has_asm))]
pub(crate) type CoreMachineType = DefaultCoreMachine<u64, WXorXMemory<SparseMemory<u64>>>;
#[cfg(has_asm)]
pub type CoreMachine = Box<AsmCoreMachine>;
#[cfg(not(has_asm))]
pub type CoreMachine = DefaultCoreMachine<u64, WXorXMemory<SparseMemory<u64>>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ScriptVersion {
V0 = 0,
V1 = 1,
}
impl ScriptVersion {
pub const fn latest() -> Self {
Self::V1
}
pub fn vm_isa(self) -> VmIsa {
match self {
Self::V0 => ISA_IMC,
Self::V1 => ISA_IMC | ISA_B | ISA_MOP,
}
}
pub fn vm_version(self) -> VmVersion {
match self {
Self::V0 => VERSION0,
Self::V1 => VERSION1,
}
}
pub fn data_hash_type(self) -> ScriptHashType {
match self {
Self::V0 => ScriptHashType::Data,
Self::V1 => ScriptHashType::Data1,
}
}
pub fn init_core_machine_without_limit(self) -> CoreMachine {
self.init_core_machine(u64::MAX)
}
pub fn init_core_machine(self, max_cycles: Cycle) -> CoreMachine {
let isa = self.vm_isa();
let version = self.vm_version();
CoreMachineType::new(isa, version, max_cycles)
}
}
#[cfg(has_asm)]
pub(crate) type Machine<'a> = AsmMachine<'a>;
#[cfg(not(has_asm))]
pub(crate) type Machine<'a> = TraceMachine<'a, CoreMachine>;
pub struct ResumableMachine<'a> {
machine: Machine<'a>,
pub(crate) program_bytes_cycles: Option<Cycle>,
}
impl<'a> ResumableMachine<'a> {
pub(crate) fn new(machine: Machine<'a>, program_bytes_cycles: Option<Cycle>) -> Self {
ResumableMachine {
machine,
program_bytes_cycles,
}
}
pub(crate) fn cycles(&self) -> Cycle {
self.machine.machine.cycles()
}
pub(crate) fn set_max_cycles(&mut self, cycles: Cycle) {
set_vm_max_cycles(&mut self.machine, cycles)
}
pub fn program_loaded(&self) -> bool {
self.program_bytes_cycles.is_none()
}
pub fn add_cycles(&mut self, cycles: Cycle) -> Result<(), VMInternalError> {
self.machine.machine.add_cycles(cycles)
}
pub fn run(&mut self) -> Result<i8, VMInternalError> {
if let Some(cycles) = self.program_bytes_cycles {
self.add_cycles(cycles)?;
self.program_bytes_cycles = None;
}
self.machine.run()
}
}
#[cfg(has_asm)]
pub(crate) fn set_vm_max_cycles(vm: &mut Machine<'_>, cycles: Cycle) {
vm.set_max_cycles(cycles)
}
#[cfg(not(has_asm))]
pub(crate) fn set_vm_max_cycles(vm: &mut Machine<'_>, cycles: Cycle) {
vm.machine.inner_mut().set_max_cycles(cycles)
}
pub struct ScriptGroup {
pub script: Script,
pub group_type: ScriptGroupType,
pub input_indices: Vec<usize>,
pub output_indices: Vec<usize>,
}
impl ScriptGroup {
pub fn new(script: &Script, group_type: ScriptGroupType) -> Self {
Self {
group_type,
script: script.to_owned(),
input_indices: vec![],
output_indices: vec![],
}
}
pub fn from_lock_script(script: &Script) -> Self {
Self::new(script, ScriptGroupType::Lock)
}
pub fn from_type_script(script: &Script) -> Self {
Self::new(script, ScriptGroupType::Type)
}
}
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ScriptGroupType {
Lock,
Type,
}
impl fmt::Display for ScriptGroupType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ScriptGroupType::Lock => write!(f, "Lock"),
ScriptGroupType::Type => write!(f, "Type"),
}
}
}
pub struct TransactionSnapshot {
pub current: usize,
pub snap: Option<(Snapshot, Cycle)>,
pub current_cycles: Cycle,
pub limit_cycles: Cycle,
}
pub struct TransactionState<'a> {
pub current: usize,
pub vm: Option<ResumableMachine<'a>>,
pub current_cycles: Cycle,
pub limit_cycles: Cycle,
}
impl TransactionState<'_> {
pub fn next_limit_cycles(&self, step_cycles: Cycle, max_cycles: Cycle) -> (Cycle, bool) {
let remain = max_cycles - self.current_cycles;
let next_limit = self.limit_cycles + step_cycles;
if next_limit < remain {
(next_limit, false)
} else {
(remain, true)
}
}
}
impl TransactionSnapshot {
pub fn next_limit_cycles(&self, step_cycles: Cycle, max_cycles: Cycle) -> (Cycle, bool) {
let remain = max_cycles - self.current_cycles;
let next_limit = self.limit_cycles + step_cycles;
if next_limit < remain {
(next_limit, false)
} else {
(remain, true)
}
}
}
impl TryFrom<TransactionState<'_>> for TransactionSnapshot {
type Error = Error;
fn try_from(state: TransactionState<'_>) -> Result<Self, Self::Error> {
let TransactionState {
current,
vm,
current_cycles,
limit_cycles,
} = state;
let (snap, current_cycles) = if let Some(mut vm) = vm {
if vm.program_loaded() {
let vm_cycles = vm.cycles();
(
Some((
make_snapshot(&mut vm.machine.machine).map_err(|e| {
ScriptError::VMInternalError(format!("{:?}", e)).unknown_source()
})?,
vm_cycles,
)),
current_cycles,
)
} else {
(None, current_cycles)
}
} else {
(None, current_cycles)
};
Ok(TransactionSnapshot {
current,
snap,
current_cycles,
limit_cycles,
})
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum VerifyResult<'a> {
Completed(Cycle),
Suspended(TransactionState<'a>),
}
impl std::fmt::Debug for TransactionSnapshot {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("TransactionSnapshot")
.field("current", &self.current)
.field("current_cycles", &self.current_cycles)
.field("limit_cycles", &self.limit_cycles)
.finish()
}
}
impl std::fmt::Debug for TransactionState<'_> {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("TransactionState")
.field("current", &self.current)
.field("current_cycles", &self.current_cycles)
.field("limit_cycles", &self.limit_cycles)
.finish()
}
}