|
|
|
@ -647,11 +647,42 @@ pub fn eval_script(
|
|
|
|
|
}
|
|
|
|
|
stack.push((v1 % v2).to_bytes());
|
|
|
|
|
},
|
|
|
|
|
Opcode::OP_RIGHT if flags.verify_right => {
|
|
|
|
|
// OP_BIN2NUM replaces OP_RIGHT
|
|
|
|
|
Opcode::OP_RIGHT if flags.verify_bin2num => {
|
|
|
|
|
let bin = stack.pop()?;
|
|
|
|
|
let n = Num::minimally_encode(&bin, 4)?;
|
|
|
|
|
stack.push(n.to_bytes());
|
|
|
|
|
},
|
|
|
|
|
// OP_NUM2BIN replaces OP_LEFT
|
|
|
|
|
Opcode::OP_LEFT if flags.verify_num2bin => {
|
|
|
|
|
let bin_size = Num::from_slice(&stack.pop()?, flags.verify_minimaldata, 4)?;
|
|
|
|
|
if bin_size.is_negative() || bin_size > MAX_SCRIPT_ELEMENT_SIZE.into() {
|
|
|
|
|
return Err(Error::PushSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let bin_size: usize = bin_size.into();
|
|
|
|
|
let num = Num::minimally_encode(&stack.pop()?, 4)?;
|
|
|
|
|
let mut num = num.to_bytes();
|
|
|
|
|
|
|
|
|
|
// check if we can fit number into array of bin_size length
|
|
|
|
|
if num.len() > bin_size {
|
|
|
|
|
return Err(Error::ImpossibleEncoding);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if we need to extend binary repr with zero-bytes
|
|
|
|
|
if num.len() < bin_size {
|
|
|
|
|
let sign_byte = num.last_mut().map(|last_byte| {
|
|
|
|
|
let sign_byte = *last_byte & 0x80;
|
|
|
|
|
*last_byte = *last_byte & 0x7f;
|
|
|
|
|
sign_byte
|
|
|
|
|
}).unwrap_or(0x00);
|
|
|
|
|
|
|
|
|
|
num.resize(bin_size - 1, 0x00);
|
|
|
|
|
num.push(sign_byte);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stack.push(num);
|
|
|
|
|
},
|
|
|
|
|
Opcode::OP_CAT | Opcode::OP_SUBSTR | Opcode::OP_LEFT | Opcode::OP_RIGHT |
|
|
|
|
|
Opcode::OP_INVERT | Opcode::OP_AND | Opcode::OP_OR | Opcode::OP_XOR |
|
|
|
|
|
Opcode::OP_2MUL | Opcode::OP_2DIV | Opcode::OP_MUL | Opcode::OP_DIV |
|
|
|
|
@ -2026,35 +2057,6 @@ mod tests {
|
|
|
|
|
basic_test(&script, result, stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn test_right(input: &[u8], result: Result<bool, Error>, output: Vec<u8>) {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
.push_bytes(input)
|
|
|
|
|
.push_opcode(Opcode::OP_RIGHT)
|
|
|
|
|
.into_script();
|
|
|
|
|
let stack = if result.is_ok() {
|
|
|
|
|
vec![output.into()].into()
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
}.into();
|
|
|
|
|
let flags = VerificationFlags::default()
|
|
|
|
|
.verify_right(true);
|
|
|
|
|
basic_test_with_flags(&script, &flags, result, stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_right_all() {
|
|
|
|
|
use error::Error;
|
|
|
|
|
|
|
|
|
|
test_right(&[0x02, 0x00, 0x00, 0x00, 0x00], Ok(true), vec![0x02]);
|
|
|
|
|
test_right(&[0x05, 0x00, 0x80], Ok(true), vec![0x85]);
|
|
|
|
|
test_right(&[0x02, 0x02, 0x02, 0x02, 0x02], Err(Error::NumberOverflow), vec![]);
|
|
|
|
|
test_right(&[0x00], Ok(false), vec![]);
|
|
|
|
|
test_right(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], Ok(true), vec![0x01]);
|
|
|
|
|
test_right(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], Ok(true), vec![0x81]);
|
|
|
|
|
test_right(&[0x80], Ok(false), vec![]);
|
|
|
|
|
test_right(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], Ok(false), vec![]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_within_invalid_stack() {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
@ -3707,4 +3709,138 @@ mod tests {
|
|
|
|
|
basic_test_with_flags(&script, &VerificationFlags::default().verify_mod(true), result,
|
|
|
|
|
vec![Num::from(3).to_bytes()].into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn op_bin2num_disabled_by_default() {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
.push_num(0.into())
|
|
|
|
|
.push_opcode(Opcode::OP_RIGHT)
|
|
|
|
|
.into_script();
|
|
|
|
|
let result = Err(Error::DisabledOpcode(Opcode::OP_RIGHT));
|
|
|
|
|
basic_test_with_flags(&script, &VerificationFlags::default(), result,
|
|
|
|
|
vec![].into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_bin2num_all() {
|
|
|
|
|
fn test_bin2num(input: &[u8], result: Result<bool, Error>, output: Vec<u8>) {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
.push_bytes(input)
|
|
|
|
|
.push_opcode(Opcode::OP_RIGHT)
|
|
|
|
|
.into_script();
|
|
|
|
|
let stack = if result.is_ok() {
|
|
|
|
|
vec![output.into()].into()
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
}.into();
|
|
|
|
|
let flags = VerificationFlags::default()
|
|
|
|
|
.verify_bin2num(true);
|
|
|
|
|
basic_test_with_flags(&script, &flags, result, stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test_bin2num(&[0x02, 0x00, 0x00, 0x00, 0x00], Ok(true), vec![0x02]);
|
|
|
|
|
test_bin2num(&[0x05, 0x00, 0x80], Ok(true), vec![0x85]);
|
|
|
|
|
test_bin2num(&[0x02, 0x02, 0x02, 0x02, 0x02], Err(Error::NumberOverflow), vec![]);
|
|
|
|
|
test_bin2num(&[0x00], Ok(false), vec![]);
|
|
|
|
|
test_bin2num(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], Ok(true), vec![0x01]);
|
|
|
|
|
test_bin2num(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], Ok(true), vec![0x81]);
|
|
|
|
|
test_bin2num(&[0x80], Ok(false), vec![]);
|
|
|
|
|
test_bin2num(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80], Ok(false), vec![]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn op_num2bin_disabled_by_default() {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
.push_num(1.into())
|
|
|
|
|
.push_num(1.into())
|
|
|
|
|
.push_opcode(Opcode::OP_LEFT)
|
|
|
|
|
.into_script();
|
|
|
|
|
let result = Err(Error::DisabledOpcode(Opcode::OP_LEFT));
|
|
|
|
|
basic_test_with_flags(&script, &VerificationFlags::default(), result,
|
|
|
|
|
vec![].into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_num2bin_all() {
|
|
|
|
|
fn test_num2bin(num: &[u8], size: &[u8], result: Result<bool, Error>, output: Vec<u8>) {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
.push_data(num)
|
|
|
|
|
.push_data(size)
|
|
|
|
|
.push_opcode(Opcode::OP_LEFT)
|
|
|
|
|
.into_script();
|
|
|
|
|
let stack = if result.is_ok() {
|
|
|
|
|
vec![output.into()].into()
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
}.into();
|
|
|
|
|
|
|
|
|
|
let flags = VerificationFlags::default()
|
|
|
|
|
.verify_num2bin(true);
|
|
|
|
|
basic_test_with_flags(&script, &flags, result, stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn test_num2bin_num(num: Num, size: Num, result: Result<bool, Error>, output: Vec<u8>) {
|
|
|
|
|
test_num2bin(&*num.to_bytes(), &*size.to_bytes(), result, output)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test_num2bin_num(256.into(), 1.into(), Err(Error::ImpossibleEncoding), vec![0x00]);
|
|
|
|
|
test_num2bin_num(1.into(), (MAX_SCRIPT_ELEMENT_SIZE + 1).into(), Err(Error::PushSize), vec![0x00]);
|
|
|
|
|
|
|
|
|
|
test_num2bin_num(0.into(), 0.into(), Ok(false), vec![]);
|
|
|
|
|
test_num2bin_num(0.into(), 1.into(), Ok(false), vec![0x00]);
|
|
|
|
|
test_num2bin_num(0.into(), 7.into(), Ok(false), vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
|
|
|
|
test_num2bin_num(1.into(), 1.into(), Ok(true), vec![0x01]);
|
|
|
|
|
test_num2bin_num((-42).into(), 1.into(), Ok(true), Num::from(-42).to_bytes().to_vec());
|
|
|
|
|
test_num2bin_num((-42).into(), 2.into(), Ok(true), vec![0x2a, 0x80]);
|
|
|
|
|
test_num2bin_num((-42).into(), 10.into(), Ok(true), vec![0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]);
|
|
|
|
|
test_num2bin_num((-42).into(), 520.into(), Ok(true), ::std::iter::once(0x2a)
|
|
|
|
|
.chain(::std::iter::repeat(0x00).take(518))
|
|
|
|
|
.chain(::std::iter::once(0x80)).collect());
|
|
|
|
|
test_num2bin_num((-42).into(), 521.into(), Err(Error::PushSize), vec![]);
|
|
|
|
|
test_num2bin_num((-42).into(), (-3).into(), Err(Error::PushSize), vec![]);
|
|
|
|
|
|
|
|
|
|
test_num2bin(&vec![0xab, 0xcd, 0xef, 0x42, 0x80], &vec![0x04], Ok(true), vec![0xab, 0xcd, 0xef, 0xc2]);
|
|
|
|
|
test_num2bin(&vec![0x80], &vec![0x00], Ok(false), vec![]);
|
|
|
|
|
test_num2bin(&vec![0x80], &vec![0x03], Ok(false), vec![0x00, 0x00, 0x00]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_num_bin_conversions_are_reverse_ops() {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
// convert num2bin
|
|
|
|
|
.push_num(123456789.into())
|
|
|
|
|
.push_num(8.into())
|
|
|
|
|
.push_opcode(Opcode::OP_LEFT)
|
|
|
|
|
// and then back bin2num
|
|
|
|
|
.push_opcode(Opcode::OP_RIGHT)
|
|
|
|
|
// check that numbers are the same
|
|
|
|
|
.push_num(123456789.into())
|
|
|
|
|
.push_opcode(Opcode::OP_EQUAL)
|
|
|
|
|
.into_script();
|
|
|
|
|
|
|
|
|
|
let flags = VerificationFlags::default()
|
|
|
|
|
.verify_num2bin(true)
|
|
|
|
|
.verify_bin2num(true);
|
|
|
|
|
basic_test_with_flags(&script, &flags, Ok(true), vec![vec![0x01].into()].into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_cat_are_reverse_ops() {
|
|
|
|
|
let script = Builder::default()
|
|
|
|
|
// split array
|
|
|
|
|
.push_data(&vec![0x01, 0x02, 0x03, 0x04, 0x05])
|
|
|
|
|
.push_num(2.into())
|
|
|
|
|
.push_opcode(Opcode::OP_SUBSTR)
|
|
|
|
|
// and then concat again
|
|
|
|
|
.push_opcode(Opcode::OP_CAT)
|
|
|
|
|
// check that numbers are the same
|
|
|
|
|
.push_data(&vec![0x01, 0x02, 0x03, 0x04, 0x05])
|
|
|
|
|
.push_opcode(Opcode::OP_EQUAL)
|
|
|
|
|
.into_script();
|
|
|
|
|
|
|
|
|
|
let flags = VerificationFlags::default()
|
|
|
|
|
.verify_concat(true)
|
|
|
|
|
.verify_split(true);
|
|
|
|
|
basic_test_with_flags(&script, &flags, Ok(true), vec![vec![0x01].into()].into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|