interpreter.rs 143 KiB
Newer Older
Marek Kotewicz's avatar
Marek Kotewicz committed
						return Err(Error::CheckSigVerify);
					},
					_ => {},
				}
			},
			Opcode::OP_RESERVED |
			Opcode::OP_VER |
			Opcode::OP_RESERVED1 |
Marek Kotewicz's avatar
Marek Kotewicz committed
			Opcode::OP_RESERVED2 => {
				if executing {
					return Err(Error::DisabledOpcode(opcode));
				}
			},
Marek Kotewicz's avatar
Marek Kotewicz committed
			Opcode::OP_VERIF |
Marek Kotewicz's avatar
Marek Kotewicz committed
			Opcode::OP_VERNOTIF => {
				return Err(Error::DisabledOpcode(opcode));
			},
Marek Kotewicz's avatar
Marek Kotewicz committed

		if stack.len() + altstack.len() > 1000 {
			return Err(Error::StackSize);
		}
Marek Kotewicz's avatar
Marek Kotewicz committed
	if !exec_stack.is_empty() {
		return Err(Error::UnbalancedConditional);
	}

	let success = !stack.is_empty() && {
		let last = stack.last()?;
		cast_to_bool(last)
	Ok(success)
Marek Kotewicz's avatar
Marek Kotewicz committed

#[cfg(test)]
mod tests {
Marek Kotewicz's avatar
Marek Kotewicz committed
	use bytes::Bytes;
Marek Kotewicz's avatar
Marek Kotewicz committed
	use chain::Transaction;
Marek Kotewicz's avatar
Marek Kotewicz committed
	use sign::SignatureVersion;
	use script::MAX_SCRIPT_ELEMENT_SIZE;
Marek Kotewicz's avatar
Marek Kotewicz committed
	use {
		Opcode, Script, ScriptWitness, VerificationFlags, Builder, Error, Num, TransactionInputSigner,
Marek Kotewicz's avatar
Marek Kotewicz committed
		NoopSignatureChecker, TransactionSignatureChecker, Stack
	use super::{eval_script, verify_script, is_public_key};
Marek Kotewicz's avatar
Marek Kotewicz committed

	#[test]
	fn tests_is_public_key() {
		assert!(!is_public_key(&[]));
		assert!(!is_public_key(&[1]));
Marek Kotewicz's avatar
Marek Kotewicz committed
		assert!(is_public_key(&Bytes::from("0495dfb90f202c7d016ef42c65bc010cd26bb8237b06253cc4d12175097bef767ed6b1fcb3caf1ed57c98d92e6cb70278721b952e29a335134857acd4c199b9d2f")));
Marek Kotewicz's avatar
Marek Kotewicz committed
		assert!(is_public_key(&[2; 33]));
		assert!(is_public_key(&[3; 33]));
		assert!(!is_public_key(&[4; 33]));
	}
Marek Kotewicz's avatar
Marek Kotewicz committed

	// https://github.com/bitcoin/bitcoin/blob/d612837814020ae832499d18e6ee5eb919a87907/src/test/script_tests.cpp#L900
	#[test]
	fn test_push_data() {
		let expected: Stack<Bytes> = vec![vec![0x5a].into()].into();
		let flags = VerificationFlags::default()
			.verify_p2sh(true);
Marek Kotewicz's avatar
Marek Kotewicz committed
		let checker = NoopSignatureChecker;
		let version = SignatureVersion::Base;
		let direct: Script = vec![Opcode::OP_PUSHBYTES_1 as u8, 0x5a].into();
		let pushdata1: Script = vec![Opcode::OP_PUSHDATA1 as u8, 0x1, 0x5a].into();
		let pushdata2: Script = vec![Opcode::OP_PUSHDATA2 as u8, 0x1, 0, 0x5a].into();
		let pushdata4: Script = vec![Opcode::OP_PUSHDATA4 as u8, 0x1, 0, 0, 0, 0x5a].into();
Marek Kotewicz's avatar
Marek Kotewicz committed
		let mut direct_stack = Stack::new();
		let mut pushdata1_stack = Stack::new();
		let mut pushdata2_stack = Stack::new();
		let mut pushdata4_stack = Stack::new();
Marek Kotewicz's avatar
Marek Kotewicz committed
		assert!(eval_script(&mut direct_stack, &direct, &flags, &checker, version).unwrap());
		assert!(eval_script(&mut pushdata1_stack, &pushdata1, &flags, &checker, version).unwrap());
		assert!(eval_script(&mut pushdata2_stack, &pushdata2, &flags, &checker, version).unwrap());
		assert!(eval_script(&mut pushdata4_stack, &pushdata4, &flags, &checker, version).unwrap());

Marek Kotewicz's avatar
Marek Kotewicz committed
		assert_eq!(direct_stack, expected);
		assert_eq!(pushdata1_stack, expected);
		assert_eq!(pushdata2_stack, expected);
		assert_eq!(pushdata4_stack, expected);
	fn basic_test_with_flags(script: &Script, flags: &VerificationFlags, expected: Result<bool, Error>, expected_stack: Stack<Bytes>) {
		let checker = NoopSignatureChecker;
		let version = SignatureVersion::Base;
Marek Kotewicz's avatar
Marek Kotewicz committed
		let mut stack = Stack::new();
		assert_eq!(eval_script(&mut stack, script, &flags, &checker, version), expected);
		if expected.is_ok() {
			assert_eq!(stack, expected_stack);
	fn basic_test(script: &Script, expected: Result<bool, Error>, expected_stack: Stack<Bytes>) {
		let flags = VerificationFlags::default()
			.verify_p2sh(true);
		basic_test_with_flags(script, &flags, expected, expected_stack)
	}

	#[test]
	fn test_equal() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_data(&[0x4])
			.push_opcode(Opcode::OP_EQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![vec![1].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_equal_false() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_data(&[0x3])
			.push_opcode(Opcode::OP_EQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![vec![0].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_equal_invalid_stack() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_opcode(Opcode::OP_EQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_equal_verify() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_data(&[0x4])
			.push_opcode(Opcode::OP_EQUALVERIFY)
			.into_script();
		let result = Ok(false);
		let stack = Stack::default();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_equal_verify_failed() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_data(&[0x3])
			.push_opcode(Opcode::OP_EQUALVERIFY)
			.into_script();
		let result = Err(Error::EqualVerify);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_equal_verify_invalid_stack() {
		let script = Builder::default()
			.push_data(&[0x4])
			.push_opcode(Opcode::OP_EQUALVERIFY)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_size() {
		let script = Builder::default()
			.push_data(&[0x12, 0x34])
			.push_opcode(Opcode::OP_SIZE)
			.into_script();
		let result = Ok(true);
		let stack = vec![vec![0x12, 0x34].into(), vec![0x2].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_size_false() {
		let script = Builder::default()
			.push_data(&[])
			.push_opcode(Opcode::OP_SIZE)
			.into_script();
		let result = Ok(false);
		let stack = vec![vec![].into(), vec![].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_size_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_SIZE)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
Marek Kotewicz's avatar
Marek Kotewicz committed

	#[test]
	fn test_hash256() {
		let script = Builder::default()
			.push_data(b"hello")
			.push_opcode(Opcode::OP_HASH256)
			.into_script();
		let result = Ok(true);
		let stack = vec!["9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50".into()].into();
Marek Kotewicz's avatar
Marek Kotewicz committed
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_hash256_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_HASH256)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
Marek Kotewicz's avatar
Marek Kotewicz committed
	}

	#[test]
	fn test_ripemd160() {
		let script = Builder::default()
			.push_data(b"hello")
			.push_opcode(Opcode::OP_RIPEMD160)
			.into_script();
		let result = Ok(true);
		let stack = vec!["108f07b8382412612c048d07d13f814118445acd".into()].into();
Marek Kotewicz's avatar
Marek Kotewicz committed
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_ripemd160_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_RIPEMD160)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
Marek Kotewicz's avatar
Marek Kotewicz committed
	}

	#[test]
	fn test_sha1() {
		let script = Builder::default()
			.push_data(b"hello")
			.push_opcode(Opcode::OP_SHA1)
			.into_script();
		let result = Ok(true);
		let stack = vec!["aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d".into()].into();
Marek Kotewicz's avatar
Marek Kotewicz committed
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_sha1_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_SHA1)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
Marek Kotewicz's avatar
Marek Kotewicz committed
	}

	#[test]
	fn test_sha256() {
		let script = Builder::default()
			.push_data(b"hello")
			.push_opcode(Opcode::OP_SHA256)
			.into_script();
		let result = Ok(true);
		let stack = vec!["2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824".into()].into();
Marek Kotewicz's avatar
Marek Kotewicz committed
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_sha256_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_SHA256)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
Marek Kotewicz's avatar
Marek Kotewicz committed
	}

	#[test]
	fn test_1add() {
		let script = Builder::default()
			.push_num(5.into())
			.push_opcode(Opcode::OP_1ADD)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(6).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_1add_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_1ADD)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_1sub() {
		let script = Builder::default()
			.push_num(5.into())
			.push_opcode(Opcode::OP_1SUB)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(4).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_1sub_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_1SUB)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_negate() {
		let script = Builder::default()
			.push_num(5.into())
			.push_opcode(Opcode::OP_NEGATE)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(-5).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_negate_negative() {
		let script = Builder::default()
			.push_num((-5).into())
			.push_opcode(Opcode::OP_NEGATE)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(5).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_negate_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_NEGATE)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_abs() {
		let script = Builder::default()
			.push_num(5.into())
			.push_opcode(Opcode::OP_ABS)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(5).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_abs_negative() {
		let script = Builder::default()
			.push_num((-5).into())
			.push_opcode(Opcode::OP_ABS)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(5).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_abs_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_ABS)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_not() {
		let script = Builder::default()
			.push_num(4.into())
			.push_opcode(Opcode::OP_NOT)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_not_zero() {
		let script = Builder::default()
			.push_num(0.into())
			.push_opcode(Opcode::OP_NOT)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_not_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_NOT)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_0notequal() {
		let script = Builder::default()
			.push_num(4.into())
			.push_opcode(Opcode::OP_0NOTEQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_0notequal_zero() {
		let script = Builder::default()
			.push_num(0.into())
			.push_opcode(Opcode::OP_0NOTEQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_0notequal_invalid_stack() {
		let script = Builder::default()
			.push_opcode(Opcode::OP_0NOTEQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_add() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_ADD)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(5).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_add_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_ADD)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_sub() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_SUB)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_sub_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_SUB)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_booland() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_BOOLAND)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_booland_first() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLAND)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_booland_second() {
		let script = Builder::default()
			.push_num(0.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_BOOLAND)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_booland_none() {
		let script = Builder::default()
			.push_num(0.into())
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLAND)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_booland_invalid_stack() {
		let script = Builder::default()
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLAND)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_boolor() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_BOOLOR)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_boolor_first() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLOR)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_boolor_second() {
		let script = Builder::default()
			.push_num(0.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_BOOLOR)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_boolor_none() {
		let script = Builder::default()
			.push_num(0.into())
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLOR)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_boolor_invalid_stack() {
		let script = Builder::default()
			.push_num(0.into())
			.push_opcode(Opcode::OP_BOOLOR)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_numequal() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMEQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_numequal_not() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_NUMEQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_numequal_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMEQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_numequalverify() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMEQUALVERIFY)
			.into_script();
		let result = Ok(false);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_numequalverify_failed() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_NUMEQUALVERIFY)
			.into_script();
		let result = Err(Error::NumEqualVerify);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_numequalverify_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMEQUALVERIFY)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	#[test]
	fn test_numnotequal() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_NUMNOTEQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_numnotequal_not() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMNOTEQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_numnotequal_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_NUMNOTEQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_lessthan() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_LESSTHAN)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_lessthan_not() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_LESSTHAN)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_lessthan_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_LESSTHAN)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_greaterthan() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHAN)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_greaterthan_not() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHAN)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_greaterthan_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHAN)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_lessthanorequal() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_LESSTHANOREQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_lessthanorequal_equal() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_LESSTHANOREQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_lessthanorequal_not() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(1.into())
			.push_opcode(Opcode::OP_LESSTHANOREQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_lessthanorequal_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_LESSTHANOREQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_greaterthanorequal() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHANOREQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_greaterthanorequal_equal() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHANOREQUAL)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(1).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_greaterthanorequal_not() {
		let script = Builder::default()
			.push_num(1.into())
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHANOREQUAL)
			.into_script();
		let result = Ok(false);
		let stack = vec![Num::from(0).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_greaterthanorequal_invalid_stack() {
		let script = Builder::default()
			.push_num(2.into())
			.push_opcode(Opcode::OP_GREATERTHANOREQUAL)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_min() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_MIN)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(2).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_min_second() {
		let script = Builder::default()
			.push_num(4.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_MIN)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(3).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_min_invalid_stack() {
		let script = Builder::default()
			.push_num(4.into())
			.push_opcode(Opcode::OP_MIN)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_max() {
		let script = Builder::default()
			.push_num(2.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_MAX)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(3).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_max_second() {
		let script = Builder::default()
			.push_num(4.into())
			.push_num(3.into())
			.push_opcode(Opcode::OP_MAX)
			.into_script();
		let result = Ok(true);
		let stack = vec![Num::from(4).to_bytes()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_max_invalid_stack() {
		let script = Builder::default()
			.push_num(4.into())
			.push_opcode(Opcode::OP_MAX)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	}

	#[test]
	fn test_within() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(2.into())
			.push_num(4.into())
			.push_opcode(Opcode::OP_WITHIN)
			.into_script();
		let result = Ok(true);
		let stack = vec![vec![1].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_within_not() {
		let script = Builder::default()
			.push_num(3.into())
			.push_num(5.into())
			.push_num(4.into())
			.push_opcode(Opcode::OP_WITHIN)
			.into_script();
		let result = Ok(false);
		let stack = vec![vec![0].into()].into();
		basic_test(&script, result, stack);
	}

	#[test]
	fn test_within_invalid_stack() {
		let script = Builder::default()
			.push_num(5.into())
			.push_num(4.into())
			.push_opcode(Opcode::OP_WITHIN)
			.into_script();
		let result = Err(Error::InvalidStackOperation);
		basic_test(&script, result, Stack::default());
	#[test]
	fn test_within_testnet_block_519() {
		let script = Builder::default()
			.push_num(1.into())
			.push_num(0.into())
			.push_num(1.into())
			.push_opcode(Opcode::OP_WITHIN)
			.push_opcode(Opcode::OP_NOT)
			.into_script();
		let result = Ok(true);
		let stack = vec![vec![1].into()].into();
		basic_test(&script, result, stack);
	}

	// https://blockchain.info/rawtx/3f285f083de7c0acabd9f106a43ec42687ab0bebe2e6f0d529db696794540fea
	#[test]
	fn test_check_transaction_signature() {
		let tx: Transaction = "0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b481000000008a47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000".into();
		let signer: TransactionInputSigner = tx.into();
		let checker = TransactionSignatureChecker {
			signer: signer,
			input_index: 0,
			input_amount: 0,
		};
		let input: Script = "47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcd".into();
		let output: Script = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into();
		let flags = VerificationFlags::default()
			.verify_p2sh(true);
		assert_eq!(verify_script(&input, &output, &ScriptWitness::default(), &flags, &checker, SignatureVersion::Base), Ok(()));