use crate::primitives::big_number::BigNumber;
use crate::script::error::ScriptError;
use crate::script::op::Op;
use crate::script::script_chunk::ScriptChunk;
use crate::script::spend::Spend;
impl Spend {
pub(crate) fn execute_opcode(
&mut self,
op: Op,
chunk: &ScriptChunk,
) -> Result<(), ScriptError> {
match op {
Op::Op0 => {
self.push_stack(vec![])?;
}
Op::Op1Negate => {
let n = BigNumber::from_number(-1);
self.push_stack(n.to_script_num())?;
}
Op::Op1 => self.push_stack(vec![1])?,
Op::Op2 => self.push_stack(vec![2])?,
Op::Op3 => self.push_stack(vec![3])?,
Op::Op4 => self.push_stack(vec![4])?,
Op::Op5 => self.push_stack(vec![5])?,
Op::Op6 => self.push_stack(vec![6])?,
Op::Op7 => self.push_stack(vec![7])?,
Op::Op8 => self.push_stack(vec![8])?,
Op::Op9 => self.push_stack(vec![9])?,
Op::Op10 => self.push_stack(vec![10])?,
Op::Op11 => self.push_stack(vec![11])?,
Op::Op12 => self.push_stack(vec![12])?,
Op::Op13 => self.push_stack(vec![13])?,
Op::Op14 => self.push_stack(vec![14])?,
Op::Op15 => self.push_stack(vec![15])?,
Op::Op16 => self.push_stack(vec![16])?,
Op::OpPushData1 | Op::OpPushData2 | Op::OpPushData4 => {
let data = chunk.data.clone().unwrap_or_default();
self.push_stack(data)?;
}
Op::OpInvalidOpcode if chunk.op_byte >= 0x01 && chunk.op_byte <= 0x4b => {
let data = chunk.data.clone().unwrap_or_default();
self.push_stack(data)?;
}
Op::OpNop | Op::OpNop1 | Op::OpNop2 | Op::OpNop3 | Op::OpNop9 | Op::OpNop10 => {
}
Op::OpIf => {
let val = self.pop_stack()?;
let cond = Self::stack_to_bool(&val);
self.if_stack.push(cond);
}
Op::OpNotIf => {
let val = self.pop_stack()?;
let cond = !Self::stack_to_bool(&val);
self.if_stack.push(cond);
}
Op::OpVerIf => {
let val = self.pop_stack()?;
let num = BigNumber::from_script_num(&val, false, None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let ver = BigNumber::from_number(self.transaction_version as i64);
let cond = num.cmp(&ver) == 0;
self.if_stack.push(cond);
}
Op::OpVerNotIf => {
let val = self.pop_stack()?;
let num = BigNumber::from_script_num(&val, false, None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let ver = BigNumber::from_number(self.transaction_version as i64);
let cond = num.cmp(&ver) != 0;
self.if_stack.push(cond);
}
Op::OpElse => {
if let Some(last) = self.if_stack.last_mut() {
*last = !*last;
} else {
return Err(ScriptError::InvalidScript(
"OP_ELSE without OP_IF".to_string(),
));
}
}
Op::OpEndIf => {
if self.if_stack.pop().is_none() {
return Err(ScriptError::InvalidScript(
"OP_ENDIF without OP_IF".to_string(),
));
}
}
Op::OpVerify => {
let val = self.pop_stack()?;
if !Self::stack_to_bool(&val) {
return Err(ScriptError::VerifyFailed);
}
}
Op::OpReturn => {
return Err(ScriptError::InvalidScript(
"OP_RETURN encountered".to_string(),
));
}
Op::OpVer => {
let ver = BigNumber::from_number(self.transaction_version as i64);
self.push_stack(ver.to_script_num())?;
}
Op::OpReserved | Op::OpReserved1 | Op::OpReserved2 => {
return Err(ScriptError::InvalidOpcode(chunk.op_byte));
}
Op::OpToAltStack => {
let val = self.pop_stack()?;
self.push_alt_stack(val)?;
}
Op::OpFromAltStack => {
let val = self.pop_alt_stack()?;
self.push_stack(val)?;
}
Op::Op2Drop => {
self.pop_stack()?;
self.pop_stack()?;
}
Op::Op2Dup => {
let a = self.stack_top(1)?.clone();
let b = self.stack_top(0)?.clone();
self.push_stack(a)?;
self.push_stack(b)?;
}
Op::Op3Dup => {
let a = self.stack_top(2)?.clone();
let b = self.stack_top(1)?.clone();
let c = self.stack_top(0)?.clone();
self.push_stack(a)?;
self.push_stack(b)?;
self.push_stack(c)?;
}
Op::Op2Over => {
let a = self.stack_top(3)?.clone();
let b = self.stack_top(2)?.clone();
self.push_stack(a)?;
self.push_stack(b)?;
}
Op::Op2Rot => {
let len = self.stack.len();
if len < 6 {
return Err(ScriptError::StackUnderflow);
}
let a = self.stack.remove(len - 6);
let b = self.stack.remove(len - 6); self.stack.push(a);
self.stack.push(b);
}
Op::Op2Swap => {
let len = self.stack.len();
if len < 4 {
return Err(ScriptError::StackUnderflow);
}
self.stack.swap(len - 4, len - 2);
self.stack.swap(len - 3, len - 1);
}
Op::OpIfDup => {
let top = self.stack_top(0)?.clone();
if Self::stack_to_bool(&top) {
self.push_stack(top)?;
}
}
Op::OpDepth => {
let depth = BigNumber::from_number(self.stack.len() as i64);
self.push_stack(depth.to_script_num())?;
}
Op::OpDrop => {
self.pop_stack()?;
}
Op::OpDup => {
let top = self.stack_top(0)?.clone();
self.push_stack(top)?;
}
Op::OpNip => {
let len = self.stack.len();
if len < 2 {
return Err(ScriptError::StackUnderflow);
}
let removed = self.stack.remove(len - 2);
self.stack_mem = self.stack_mem.saturating_sub(removed.len());
}
Op::OpOver => {
let item = self.stack_top(1)?.clone();
self.push_stack(item)?;
}
Op::OpPick => {
let n_bytes = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n_val = n.to_number().unwrap_or(0) as isize;
if n_val < 0 {
return Err(ScriptError::InvalidStackOperation(
"negative pick".to_string(),
));
}
let item = self.stack_top(n_val)?.clone();
self.push_stack(item)?;
}
Op::OpRoll => {
let n_bytes = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n_val = n.to_number().unwrap_or(0) as usize;
let len = self.stack.len();
if n_val >= len {
return Err(ScriptError::StackUnderflow);
}
let idx = len - 1 - n_val;
let item = self.stack.remove(idx);
self.stack.push(item);
}
Op::OpRot => {
let len = self.stack.len();
if len < 3 {
return Err(ScriptError::StackUnderflow);
}
let item = self.stack.remove(len - 3);
self.stack.push(item);
}
Op::OpSwap => {
let len = self.stack.len();
if len < 2 {
return Err(ScriptError::StackUnderflow);
}
self.stack.swap(len - 2, len - 1);
}
Op::OpTuck => {
let len = self.stack.len();
if len < 2 {
return Err(ScriptError::StackUnderflow);
}
let top = self.stack[len - 1].clone();
self.ensure_stack_mem(top.len())?;
self.stack_mem += top.len();
self.stack.insert(len - 2, top);
}
Op::OpCat => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
let mut result = a;
result.extend_from_slice(&b);
self.push_stack(result)?;
}
Op::OpSplit => {
let n_bytes = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let pos = n.to_number().unwrap_or(0) as usize;
let data = self.pop_stack()?;
if pos > data.len() {
return Err(ScriptError::InvalidStackOperation(
"split position out of range".to_string(),
));
}
let left = data[..pos].to_vec();
let right = data[pos..].to_vec();
self.push_stack(left)?;
self.push_stack(right)?;
}
Op::OpNum2Bin => {
let size_bytes = self.pop_stack()?;
let size = BigNumber::from_script_num(&size_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let size_val = size.to_number().unwrap_or(0) as usize;
let num_bytes = self.pop_stack()?;
let num = BigNumber::from_script_num(&num_bytes, false, None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let mut result = num.to_script_num();
if result.len() > size_val {
return Err(ScriptError::InvalidStackOperation(
"num2bin: number too large for target size".to_string(),
));
}
if !result.is_empty() {
let sign_byte = result.last().unwrap() & 0x80;
let last_idx = result.len() - 1;
result[last_idx] &= 0x7f; while result.len() < size_val {
result.push(0x00);
}
if size_val > 0 {
let last = result.len() - 1;
result[last] |= sign_byte; }
} else {
result.resize(size_val, 0x00);
}
self.push_stack(result)?;
}
Op::OpBin2Num => {
let data = self.pop_stack()?;
let num = BigNumber::from_script_num(&data, false, None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
self.push_stack(num.to_script_num())?;
}
Op::OpSize => {
let top = self.stack_top(0)?;
let size = BigNumber::from_number(top.len() as i64);
self.push_stack(size.to_script_num())?;
}
Op::OpInvert => {
let a = self.pop_stack()?;
let result: Vec<u8> = a.iter().map(|b| !b).collect();
self.push_stack(result)?;
}
Op::OpAnd => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
if a.len() != b.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_AND: operands must have same length".to_string(),
));
}
let result: Vec<u8> = a.iter().zip(b.iter()).map(|(x, y)| x & y).collect();
self.push_stack(result)?;
}
Op::OpOr => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
if a.len() != b.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_OR: operands must have same length".to_string(),
));
}
let result: Vec<u8> = a.iter().zip(b.iter()).map(|(x, y)| x | y).collect();
self.push_stack(result)?;
}
Op::OpXor => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
if a.len() != b.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_XOR: operands must have same length".to_string(),
));
}
let result: Vec<u8> = a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect();
self.push_stack(result)?;
}
Op::OpEqual => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
let equal = a == b;
self.push_stack(Self::bool_to_stack(equal))?;
}
Op::OpEqualVerify => {
let b = self.pop_stack()?;
let a = self.pop_stack()?;
if a != b {
return Err(ScriptError::EqualVerifyFailed);
}
}
Op::Op1Add => {
self.unary_arith_op(|a| a.add(&BigNumber::from_number(1)))?;
}
Op::Op1Sub => {
self.unary_arith_op(|a| a.sub(&BigNumber::from_number(1)))?;
}
Op::OpNegate => {
self.unary_arith_op(|a| a.neg())?;
}
Op::OpAbs => {
self.unary_arith_op(|a| if a.is_negative() { a.neg() } else { a })?;
}
Op::OpNot => {
let val = self.pop_stack()?;
let num = BigNumber::from_script_num(&val, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = if num.is_zero() {
BigNumber::from_number(1)
} else {
BigNumber::from_number(0)
};
self.push_stack(result.to_script_num())?;
}
Op::Op0NotEqual => {
let val = self.pop_stack()?;
let num = BigNumber::from_script_num(&val, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = if num.is_zero() {
BigNumber::from_number(0)
} else {
BigNumber::from_number(1)
};
self.push_stack(result.to_script_num())?;
}
Op::OpAdd => {
self.binary_arith_op(|a, b| a.add(&b))?;
}
Op::OpSub => {
self.binary_arith_op(|a, b| a.sub(&b))?;
}
Op::OpMul => {
self.binary_arith_op(|a, b| a.mul(&b))?;
}
Op::OpDiv => {
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
if b.is_zero() {
return Err(ScriptError::DivisionByZero);
}
let (result, _) = a
.div_mod(&b)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
self.push_stack(result.to_script_num())?;
}
Op::OpMod => {
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
if b.is_zero() {
return Err(ScriptError::DivisionByZero);
}
let (_, result) = a
.div_mod(&b)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
self.push_stack(result.to_script_num())?;
}
Op::OpBoolAnd => {
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = if !a.is_zero() && !b.is_zero() {
BigNumber::from_number(1)
} else {
BigNumber::from_number(0)
};
self.push_stack(result.to_script_num())?;
}
Op::OpBoolOr => {
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = if !a.is_zero() || !b.is_zero() {
BigNumber::from_number(1)
} else {
BigNumber::from_number(0)
};
self.push_stack(result.to_script_num())?;
}
Op::OpNumEqual => {
self.binary_cmp_op(|a, b| a.cmp(b) == 0)?;
}
Op::OpNumEqualVerify => {
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
if a.cmp(&b) != 0 {
return Err(ScriptError::NumEqualVerifyFailed);
}
}
Op::OpNumNotEqual => {
self.binary_cmp_op(|a, b| a.cmp(b) != 0)?;
}
Op::OpLessThan => {
self.binary_cmp_op(|a, b| a.cmp(b) < 0)?;
}
Op::OpGreaterThan => {
self.binary_cmp_op(|a, b| a.cmp(b) > 0)?;
}
Op::OpLessThanOrEqual => {
self.binary_cmp_op(|a, b| a.cmp(b) <= 0)?;
}
Op::OpGreaterThanOrEqual => {
self.binary_cmp_op(|a, b| a.cmp(b) >= 0)?;
}
Op::OpMin => {
self.binary_arith_op(|a, b| if a.cmp(&b) < 0 { a } else { b })?;
}
Op::OpMax => {
self.binary_arith_op(|a, b| if a.cmp(&b) > 0 { a } else { b })?;
}
Op::OpWithin => {
let max_bytes = self.pop_stack()?;
let min_bytes = self.pop_stack()?;
let x_bytes = self.pop_stack()?;
let x = BigNumber::from_script_num(&x_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let min = BigNumber::from_script_num(&min_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let max = BigNumber::from_script_num(&max_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = x.cmp(&min) >= 0 && x.cmp(&max) < 0;
self.push_stack(Self::bool_to_stack(result))?;
}
Op::OpLShift => {
let n_bytes = self.pop_stack()?;
let data = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let shift = n.to_number().unwrap_or(0);
if shift < 0 {
return Err(ScriptError::InvalidStackOperation(
"negative shift".to_string(),
));
}
let result = byte_lshift(&data, shift as usize);
self.push_stack(result)?;
}
Op::OpRShift => {
let n_bytes = self.pop_stack()?;
let data = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let shift = n.to_number().unwrap_or(0);
if shift < 0 {
return Err(ScriptError::InvalidStackOperation(
"negative shift".to_string(),
));
}
let result = byte_rshift(&data, shift as usize);
self.push_stack(result)?;
}
Op::OpRipemd160 => {
let data = self.pop_stack()?;
let hash = crate::primitives::hash::ripemd160(&data);
self.push_stack(hash.to_vec())?;
}
Op::OpSha1 => {
let data = self.pop_stack()?;
let hash = crate::primitives::hash::sha1(&data);
self.push_stack(hash.to_vec())?;
}
Op::OpSha256 => {
let data = self.pop_stack()?;
let hash = crate::primitives::hash::sha256(&data);
self.push_stack(hash.to_vec())?;
}
Op::OpHash160 => {
let data = self.pop_stack()?;
let hash = crate::primitives::hash::hash160(&data);
self.push_stack(hash.to_vec())?;
}
Op::OpHash256 => {
let data = self.pop_stack()?;
let hash = crate::primitives::hash::hash256(&data);
self.push_stack(hash.to_vec())?;
}
Op::OpCodeSeparator => {
self.last_code_separator = Some(self.program_counter);
}
Op::OpCheckSig => {
self.op_checksig()?;
}
Op::OpCheckSigVerify => {
self.op_checksig()?;
let val = self.pop_stack()?;
if !Self::stack_to_bool(&val) {
return Err(ScriptError::CheckSigVerifyFailed);
}
}
Op::OpCheckMultiSig => {
self.op_checkmultisig()?;
}
Op::OpCheckMultiSigVerify => {
self.op_checkmultisig()?;
let val = self.pop_stack()?;
if !Self::stack_to_bool(&val) {
return Err(ScriptError::CheckMultiSigVerifyFailed);
}
}
Op::OpSubstr => {
let len_bytes = self.pop_stack()?;
let begin_bytes = self.pop_stack()?;
let data = self.pop_stack()?;
let begin = BigNumber::from_script_num(&begin_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let len = BigNumber::from_script_num(&len_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let begin_val = begin.to_number().unwrap_or(0) as usize;
let len_val = len.to_number().unwrap_or(0) as usize;
if begin_val + len_val > data.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_SUBSTR: range out of bounds".to_string(),
));
}
let result = data[begin_val..begin_val + len_val].to_vec();
self.push_stack(result)?;
}
Op::OpLeft => {
let n_bytes = self.pop_stack()?;
let data = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n_val = n.to_number().unwrap_or(0) as usize;
if n_val > data.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_LEFT: position out of range".to_string(),
));
}
self.push_stack(data[..n_val].to_vec())?;
}
Op::OpRight => {
let n_bytes = self.pop_stack()?;
let data = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n_val = n.to_number().unwrap_or(0) as usize;
if n_val > data.len() {
return Err(ScriptError::InvalidStackOperation(
"OP_RIGHT: position out of range".to_string(),
));
}
self.push_stack(data[data.len() - n_val..].to_vec())?;
}
Op::OpLShiftNum => {
let n_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let shift = n.to_number().unwrap_or(0);
if shift < 0 {
return Err(ScriptError::InvalidStackOperation(
"negative shift".to_string(),
));
}
let mut result = a;
result.iushln(shift as usize);
self.push_stack(result.to_script_num())?;
}
Op::OpRShiftNum => {
let n_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let shift = n.to_number().unwrap_or(0);
if shift < 0 {
return Err(ScriptError::InvalidStackOperation(
"negative shift".to_string(),
));
}
let mut result = a;
result.iushrn(shift as usize);
self.push_stack(result.to_script_num())?;
}
Op::Op2Mul => {
return Err(ScriptError::DisabledOpcode("OP_2MUL".to_string()));
}
Op::Op2Div => {
return Err(ScriptError::DisabledOpcode("OP_2DIV".to_string()));
}
_ => {
let byte = chunk.op_byte;
if (0xba..=0xf8).contains(&byte) {
} else {
return Err(ScriptError::InvalidOpcode(byte));
}
}
}
Ok(())
}
fn unary_arith_op<F>(&mut self, f: F) -> Result<(), ScriptError>
where
F: FnOnce(BigNumber) -> BigNumber,
{
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = f(a);
self.push_stack(result.to_script_num())?;
Ok(())
}
fn binary_arith_op<F>(&mut self, f: F) -> Result<(), ScriptError>
where
F: FnOnce(BigNumber, BigNumber) -> BigNumber,
{
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = f(a, b);
self.push_stack(result.to_script_num())?;
Ok(())
}
fn binary_cmp_op<F>(&mut self, f: F) -> Result<(), ScriptError>
where
F: FnOnce(&BigNumber, &BigNumber) -> bool,
{
let b_bytes = self.pop_stack()?;
let a_bytes = self.pop_stack()?;
let a = BigNumber::from_script_num(&a_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let b = BigNumber::from_script_num(&b_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let result = f(&a, &b);
self.push_stack(Self::bool_to_stack(result))?;
Ok(())
}
fn op_checksig(&mut self) -> Result<(), ScriptError> {
use crate::primitives::ecdsa::ecdsa_verify;
use crate::primitives::point::Point;
use crate::primitives::signature::Signature;
let pubkey_bytes = self.pop_stack()?;
let sig_bytes = self.pop_stack()?;
if sig_bytes.is_empty() {
self.push_stack(Self::bool_to_stack(false))?;
return Ok(());
}
let sighash_type = *sig_bytes.last().unwrap() as u32;
let der_sig = &sig_bytes[..sig_bytes.len() - 1];
let signature = match Signature::from_der(der_sig) {
Ok(s) => s,
Err(_) => {
self.push_stack(Self::bool_to_stack(false))?;
return Ok(());
}
};
if !self.is_relaxed() && !signature.has_low_s() {
self.push_stack(Self::bool_to_stack(false))?;
return Ok(());
}
let pubkey = match Point::from_der(&pubkey_bytes) {
Ok(p) => p,
Err(_) => {
self.push_stack(Self::bool_to_stack(false))?;
return Ok(());
}
};
let sub_script = self.get_subscript();
let preimage = self.sighash_preimage(&sub_script, sighash_type);
let sighash = crate::primitives::hash::hash256(&preimage);
let valid = ecdsa_verify(&sighash, &signature, &pubkey);
self.push_stack(Self::bool_to_stack(valid))?;
Ok(())
}
fn op_checkmultisig(&mut self) -> Result<(), ScriptError> {
use crate::primitives::ecdsa::ecdsa_verify;
use crate::primitives::point::Point;
use crate::primitives::signature::Signature;
let n_bytes = self.pop_stack()?;
let n = BigNumber::from_script_num(&n_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let n_val = n.to_number().unwrap_or(0) as usize;
if n_val > 20 {
return Err(ScriptError::InvalidScript(
"too many public keys".to_string(),
));
}
let mut pubkeys = Vec::with_capacity(n_val);
for _ in 0..n_val {
pubkeys.push(self.pop_stack()?);
}
let m_bytes = self.pop_stack()?;
let m = BigNumber::from_script_num(&m_bytes, !self.is_relaxed(), None)
.map_err(|e| ScriptError::InvalidScript(e.to_string()))?;
let m_val = m.to_number().unwrap_or(0) as usize;
if m_val > n_val {
return Err(ScriptError::InvalidScript(
"m > n in CHECKMULTISIG".to_string(),
));
}
let mut signatures = Vec::with_capacity(m_val);
for _ in 0..m_val {
signatures.push(self.pop_stack()?);
}
let dummy = self.pop_stack()?;
if !self.is_relaxed() && !dummy.is_empty() {
return Err(ScriptError::NullDummyViolation);
}
let sub_script = self.get_subscript();
let mut pk_idx = 0;
let mut sig_idx = 0;
let mut success = true;
while sig_idx < m_val && pk_idx < n_val {
let sig_bytes = &signatures[sig_idx];
if sig_bytes.is_empty() {
sig_idx += 1;
continue;
}
let sighash_type = *sig_bytes.last().unwrap() as u32;
let der_sig = &sig_bytes[..sig_bytes.len() - 1];
let signature = match Signature::from_der(der_sig) {
Ok(s) => s,
Err(_) => {
success = false;
break;
}
};
if !self.is_relaxed() && !signature.has_low_s() {
success = false;
break;
}
let pubkey = match Point::from_der(&pubkeys[pk_idx]) {
Ok(p) => p,
Err(_) => {
pk_idx += 1;
continue;
}
};
let preimage = self.sighash_preimage(&sub_script, sighash_type);
let sighash = crate::primitives::hash::hash256(&preimage);
if ecdsa_verify(&sighash, &signature, &pubkey) {
sig_idx += 1;
}
pk_idx += 1;
if n_val - pk_idx < m_val - sig_idx {
success = false;
break;
}
}
if sig_idx < m_val {
success = false;
}
self.push_stack(Self::bool_to_stack(success))?;
Ok(())
}
fn get_subscript(&self) -> crate::script::script::Script {
use crate::script::script::Script;
let chunks = self.locking_script.chunks();
let start = match self.last_code_separator {
Some(pos) => pos + 1,
None => 0,
};
if start >= chunks.len() {
return Script::new();
}
Script::from_chunks(chunks[start..].to_vec())
}
pub(crate) fn sighash_preimage(
&self,
sub_script: &crate::script::script::Script,
sighash_type: u32,
) -> Vec<u8> {
use crate::primitives::hash::hash256;
let sighash_forkid: u32 = 0x40;
let sighash_anyonecanpay: u32 = 0x80;
let base_type = sighash_type & 0x1f;
let _sighash_all: u32 = 1;
let sighash_none: u32 = 2;
let sighash_single: u32 = 3;
let anyone_can_pay = (sighash_type & sighash_anyonecanpay) != 0;
let mut preimage = Vec::new();
preimage.extend_from_slice(&self.transaction_version.to_le_bytes());
if !anyone_can_pay {
let mut prevouts = Vec::new();
prevouts.extend_from_slice(&txid_to_bytes(&self.source_txid));
prevouts.extend_from_slice(&(self.source_output_index as u32).to_le_bytes());
for input in &self.other_inputs {
let default_txid = "00".repeat(32);
let txid = input.source_txid.as_deref().unwrap_or(&default_txid);
prevouts.extend_from_slice(&txid_to_bytes(txid));
prevouts.extend_from_slice(&input.source_output_index.to_le_bytes());
}
preimage.extend_from_slice(&hash256(&prevouts));
} else {
preimage.extend_from_slice(&[0u8; 32]);
}
if !anyone_can_pay && base_type != sighash_none && base_type != sighash_single {
let mut sequences = Vec::new();
sequences.extend_from_slice(&self.transaction_sequence.to_le_bytes());
for input in &self.other_inputs {
sequences.extend_from_slice(&input.sequence.to_le_bytes());
}
preimage.extend_from_slice(&hash256(&sequences));
} else {
preimage.extend_from_slice(&[0u8; 32]);
}
preimage.extend_from_slice(&txid_to_bytes(&self.source_txid));
preimage.extend_from_slice(&(self.source_output_index as u32).to_le_bytes());
let script_bytes = sub_script.to_binary();
write_varint(&mut preimage, script_bytes.len() as u64);
preimage.extend_from_slice(&script_bytes);
preimage.extend_from_slice(&self.source_satoshis.to_le_bytes());
preimage.extend_from_slice(&self.transaction_sequence.to_le_bytes());
if base_type != sighash_none && base_type != sighash_single {
let mut outputs = Vec::new();
for output in &self.other_outputs {
outputs.extend_from_slice(&output.satoshis.unwrap_or(0).to_le_bytes());
let script_bytes = output.locking_script.to_binary();
write_varint(&mut outputs, script_bytes.len() as u64);
outputs.extend_from_slice(&script_bytes);
}
preimage.extend_from_slice(&hash256(&outputs));
} else if base_type == sighash_single && self.input_index < self.other_outputs.len() {
let output = &self.other_outputs[self.input_index];
let mut out_data = Vec::new();
out_data.extend_from_slice(&output.satoshis.unwrap_or(0).to_le_bytes());
let script_bytes = output.locking_script.to_binary();
write_varint(&mut out_data, script_bytes.len() as u64);
out_data.extend_from_slice(&script_bytes);
preimage.extend_from_slice(&hash256(&out_data));
} else {
preimage.extend_from_slice(&[0u8; 32]);
}
preimage.extend_from_slice(&self.transaction_lock_time.to_le_bytes());
preimage.extend_from_slice(&(sighash_type | sighash_forkid).to_le_bytes());
preimage
}
}
#[allow(clippy::needless_range_loop)]
fn byte_lshift(data: &[u8], n: usize) -> Vec<u8> {
if data.is_empty() || n >= data.len() * 8 {
return vec![0; data.len()];
}
let byte_shift = n / 8;
let bit_shift = n % 8;
let len = data.len();
let mut result = vec![0u8; len];
for i in 0..len {
let src = i + byte_shift;
if src < len {
result[i] = data[src] << bit_shift;
if bit_shift > 0 && src + 1 < len {
result[i] |= data[src + 1] >> (8 - bit_shift);
}
}
}
result
}
fn byte_rshift(data: &[u8], n: usize) -> Vec<u8> {
if data.is_empty() || n >= data.len() * 8 {
return vec![0; data.len()];
}
let byte_shift = n / 8;
let bit_shift = n % 8;
let len = data.len();
let mut result = vec![0u8; len];
for i in (0..len).rev() {
if i >= byte_shift {
let src = i - byte_shift;
result[i] = data[src] >> bit_shift;
if bit_shift > 0 && src > 0 {
result[i] |= data[src - 1] << (8 - bit_shift);
}
}
}
result
}
fn txid_to_bytes(txid: &str) -> Vec<u8> {
let mut bytes = Vec::with_capacity(32);
for i in (0..txid.len()).step_by(2) {
if let Ok(b) = u8::from_str_radix(&txid[i..i + 2], 16) {
bytes.push(b);
}
}
bytes.reverse();
bytes.resize(32, 0);
bytes
}
fn write_varint(buf: &mut Vec<u8>, val: u64) {
if val < 0xfd {
buf.push(val as u8);
} else if val <= 0xffff {
buf.push(0xfd);
buf.extend_from_slice(&(val as u16).to_le_bytes());
} else if val <= 0xffff_ffff {
buf.push(0xfe);
buf.extend_from_slice(&(val as u32).to_le_bytes());
} else {
buf.push(0xff);
buf.extend_from_slice(&val.to_le_bytes());
}
}