use crate::ScriptError;
use ckb_error::Error;
use ckb_types::{
core::Cycle,
packed::{Byte32, Script},
};
use ckb_vm::{
machine::{VERSION0, VERSION1},
snapshot::{make_snapshot, Snapshot},
SupportMachine, ISA_B, ISA_IMC, ISA_MOP,
};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
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 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> {
pub(crate) machine: Machine<'a>,
pub(crate) program_loaded: bool,
}
impl<'a> ResumableMachine<'a> {
pub(crate) fn new(machine: Machine<'a>, program_loaded: bool) -> Self {
ResumableMachine {
machine,
program_loaded,
}
}
pub(crate) fn cycles(&self) -> Cycle {
self.machine.machine.cycles()
}
#[cfg(test)]
pub(crate) fn set_cycles(&mut self, cycles: Cycle) {
self.machine.machine.set_cycles(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_loaded
}
}
#[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: (ScriptGroupType, Byte32),
pub remain: Vec<(ScriptGroupType, Byte32)>,
pub snap: Option<Snapshot>,
pub current_cycles: Cycle,
pub limit_cycles: Cycle,
}
pub struct TransactionState<'a> {
pub current: (ScriptGroupType, Byte32),
pub remain: Vec<(ScriptGroupType, Byte32)>,
pub vm: 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,
remain,
mut vm,
current_cycles,
limit_cycles,
} = state;
let snap =
if vm.program_loaded {
Some(make_snapshot(&mut vm.machine.machine).map_err(|e| {
ScriptError::VMInternalError(format!("{:?}", e)).unknown_source()
})?)
} else {
None
};
Ok(TransactionSnapshot {
current,
remain,
snap,
current_cycles,
limit_cycles,
})
}
}
#[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("remain", &self.remain)
.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("remain", &self.remain)
.field("current_cycles", &self.current_cycles)
.field("limit_cycles", &self.limit_cycles)
.finish()
}
}