use mini_bitcoin_script::error::ScriptError;
use mini_bitcoin_script::opcode::Opcode;
use mini_bitcoin_script::token::Token;
use mini_bitcoin_script::tokenizer::{parse_script, parse_script_hex};
#[test]
fn empty_script() {
let tokens = parse_script(&[]).unwrap();
assert!(tokens.is_empty());
}
#[test]
fn single_opcode() {
let tokens = parse_script(&[0x76]).unwrap();
assert_eq!(tokens, vec![Token::Op(Opcode::OpDup)]);
}
#[test]
fn direct_push_3_bytes() {
let tokens = parse_script(&[0x03, 0xaa, 0xbb, 0xcc]).unwrap();
assert_eq!(tokens, vec![Token::PushData(vec![0xaa, 0xbb, 0xcc])]);
}
#[test]
fn pushdata1() {
let tokens = parse_script(&[0x4c, 0x03, 0xaa, 0xbb, 0xcc]).unwrap();
assert_eq!(tokens, vec![Token::PushData(vec![0xaa, 0xbb, 0xcc])]);
}
#[test]
fn pushdata2() {
let tokens = parse_script(&[0x4d, 0x03, 0x00, 0xaa, 0xbb, 0xcc]).unwrap();
assert_eq!(tokens, vec![Token::PushData(vec![0xaa, 0xbb, 0xcc])]);
}
#[test]
fn pushdata4() {
let tokens = parse_script(&[0x4e, 0x03, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc]).unwrap();
assert_eq!(tokens, vec![Token::PushData(vec![0xaa, 0xbb, 0xcc])]);
}
#[test]
fn truncated_direct_push() {
let err = parse_script(&[0x03, 0xaa]).unwrap_err();
assert_eq!(err, ScriptError::UnexpectedEndOfScript);
}
#[test]
fn op0_parses() {
let tokens = parse_script(&[0x00]).unwrap();
assert_eq!(tokens, vec![Token::Op(Opcode::Op0)]);
}
#[test]
fn op1_through_op16() {
for (i, byte) in (0x51u8..=0x60).enumerate() {
let tokens = parse_script(&[byte]).unwrap();
let expected_opcode = match i {
0 => Opcode::Op1,
1 => Opcode::Op2,
2 => Opcode::Op3,
3 => Opcode::Op4,
4 => Opcode::Op5,
5 => Opcode::Op6,
6 => Opcode::Op7,
7 => Opcode::Op8,
8 => Opcode::Op9,
9 => Opcode::Op10,
10 => Opcode::Op11,
11 => Opcode::Op12,
12 => Opcode::Op13,
13 => Opcode::Op14,
14 => Opcode::Op15,
15 => Opcode::Op16,
_ => unreachable!(),
};
assert_eq!(
tokens,
vec![Token::Op(expected_opcode)],
"failed for byte 0x{byte:02x}"
);
}
}
#[test]
fn p2pkh_script_pubkey() {
let mut script = vec![0x76, 0xa9, 0x14]; let hash: [u8; 20] = [0xab; 20];
script.extend_from_slice(&hash);
script.push(0x88); script.push(0xac);
let tokens = parse_script(&script).unwrap();
assert_eq!(tokens.len(), 5);
assert_eq!(tokens[0], Token::Op(Opcode::OpDup));
assert_eq!(tokens[1], Token::Op(Opcode::OpHash160));
assert_eq!(tokens[2], Token::PushData(hash.to_vec()));
assert_eq!(tokens[3], Token::Op(Opcode::OpEqualVerify));
assert_eq!(tokens[4], Token::Op(Opcode::OpCheckSig));
}
#[test]
fn unsupported_opcode() {
let err = parse_script(&[0xb0]).unwrap_err();
assert_eq!(err, ScriptError::UnsupportedOpcode(0xb0));
}
#[test]
fn parse_script_hex_valid() {
let tokens_hex = parse_script_hex("515187").unwrap();
let tokens_raw = parse_script(&[0x51, 0x51, 0x87]).unwrap();
assert_eq!(tokens_hex, tokens_raw);
}
#[test]
fn parse_script_hex_invalid() {
let err = parse_script_hex("zzzz").unwrap_err();
assert_eq!(err, ScriptError::InvalidHex);
}
#[test]
fn pushdata1_truncated_length() {
let err = parse_script(&[0x4c]).unwrap_err();
assert_eq!(err, ScriptError::UnexpectedEndOfScript);
}
#[test]
fn op1negate_parses() {
let tokens = parse_script(&[0x4f]).unwrap();
assert_eq!(tokens, vec![Token::Op(Opcode::Op1Negate)]);
}
#[test]
fn multi_token_script() {
let tokens = parse_script(&[0x51, 0x51, 0x87]).unwrap();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens[0], Token::Op(Opcode::Op1));
assert_eq!(tokens[1], Token::Op(Opcode::Op1));
assert_eq!(tokens[2], Token::Op(Opcode::OpEqual));
}