use crate::{ScriptError, ScriptGroup};
use ckb_hash::new_blake2b;
use ckb_types::{
core::{cell::ResolvedTransaction, Cycle},
prelude::*,
};
pub const TYPE_ID_CYCLES: Cycle = 1_000_000;
pub const ERROR_ARGS: i8 = -1;
pub const ERROR_TOO_MANY_CELLS: i8 = -2;
pub const ERROR_INVALID_INPUT_HASH: i8 = -3;
pub struct TypeIdSystemScript<'a> {
pub rtx: &'a ResolvedTransaction,
pub script_group: &'a ScriptGroup,
pub max_cycles: Cycle,
}
impl<'a> TypeIdSystemScript<'a> {
pub fn verify(&self) -> Result<Cycle, ScriptError> {
if self.max_cycles < TYPE_ID_CYCLES {
return Err(ScriptError::ExceededMaximumCycles(self.max_cycles));
}
if self.script_group.script.args().len() != 32 {
return Err(self.validation_failure(ERROR_ARGS));
}
if self.script_group.input_indices.len() > 1 || self.script_group.output_indices.len() > 1 {
return Err(self.validation_failure(ERROR_TOO_MANY_CELLS));
}
if self.script_group.input_indices.is_empty() {
let first_cell_input = self
.rtx
.transaction
.inputs()
.get(0)
.ok_or_else(|| self.validation_failure(ERROR_ARGS))?;
let first_output_index: u64 = self
.script_group
.output_indices
.get(0)
.map(|output_index| *output_index as u64)
.ok_or_else(|| self.validation_failure(ERROR_ARGS))?;
let mut blake2b = new_blake2b();
blake2b.update(first_cell_input.as_slice());
blake2b.update(&first_output_index.to_le_bytes());
let mut ret = [0; 32];
blake2b.finalize(&mut ret);
if ret[..] != self.script_group.script.args().raw_data()[..] {
return Err(self.validation_failure(ERROR_INVALID_INPUT_HASH));
}
}
Ok(TYPE_ID_CYCLES)
}
fn validation_failure(&self, exit_code: i8) -> ScriptError {
ScriptError::validation_failure(&self.script_group.script, exit_code)
}
}