use std::collections::BTreeMap;
use bitcoin::script::{Instruction, Script};
use bitcoin::opcodes::all::{OP_PUSHBYTES_0, OP_IF, OP_ENDIF};
use bitcoin::{Transaction, Witness};
use crate::error::{Error, Result};
use crate::inscription::Inscription;
pub const PROTOCOL_ID: [u8; 7] = *b"ord\x01\0\0\0";
pub const BODY_TAG: [u8; 0] = [];
fn is_empty_push(instruction: &Instruction) -> bool {
match instruction {
Instruction::PushBytes(bytes) => bytes.is_empty(),
Instruction::Op(OP_PUSHBYTES_0) => true,
_ => false,
}
}
pub fn parse_transaction_inscriptions(tx: &Transaction) -> Vec<Inscription> {
tx.input
.iter()
.enumerate()
.flat_map(|(input_index, input)| {
parse_input_inscriptions(input_index, &input.witness)
})
.collect()
}
pub fn is_inscription_input(witness: &Witness, i: usize) -> bool {
if witness.len() <= i {
eprintln!("[DEBUG:is_inscription_input] Witness length {} is less than requested index {}", witness.len(), i);
return false;
}
let script_bytes = witness.nth(i).unwrap();
eprintln!("[DEBUG:is_inscription_input] Script bytes length: {}", script_bytes.len());
let script = Script::from_bytes(script_bytes);
let instructions_result = script.instructions().collect::<std::result::Result<Vec<_>, _>>();
let mut instructions = match instructions_result {
Ok(inst) => {
eprintln!("[DEBUG:is_inscription_input] Successfully parsed {} script instructions", inst.len());
inst.into_iter()
},
Err(e) => {
eprintln!("[DEBUG:is_inscription_input] Failed to parse script instructions: {:?}", e);
return false;
}
};
match instructions.next() {
Some(Instruction::Op(OP_PUSHBYTES_0)) => {
eprintln!("[DEBUG:is_inscription_input] Found OP_0");
},
other => {
eprintln!("[DEBUG:is_inscription_input] Expected OP_0, got {:?}", other);
return false;
}
}
match instructions.next() {
Some(Instruction::Op(OP_IF)) => {
eprintln!("[DEBUG:is_inscription_input] Found OP_IF");
},
other => {
eprintln!("[DEBUG:is_inscription_input] Expected OP_IF, got {:?}", other);
return false;
}
}
match instructions.next() {
Some(Instruction::PushBytes(bytes)) if bytes.as_bytes() == PROTOCOL_ID => {
eprintln!("[DEBUG:is_inscription_input] Found protocol identifier");
},
other => {
eprintln!("[DEBUG:is_inscription_input] Expected protocol identifier, got {:?}", other);
return false;
}
}
eprintln!("[DEBUG:is_inscription_input] Script contains a valid inscription pattern");
true
}
pub fn parse_input_inscriptions(input_index: usize, witness: &Witness) -> Vec<Inscription> {
if witness.len() == 0 {
return Vec::new();
}
let mut all_inscriptions = Vec::new();
for witness_index in 0..witness.len() {
if let Some(script_bytes) = witness.nth(witness_index) {
let script = Script::from_bytes(script_bytes);
let instructions_result = script.instructions().collect::<std::result::Result<Vec<_>, _>>();
let instructions = match instructions_result {
Ok(instructions) => instructions,
Err(_) => continue,
};
if instructions.len() >= 5 {
let mut contains_ord_marker = false;
let mut content_type = None;
let mut content_data = None;
for i in 0..instructions.len() - 3 {
match &instructions[i..i+4] {
[Instruction::PushBytes(empty), Instruction::Op(OP_IF),
Instruction::PushBytes(ord_marker), Instruction::PushBytes(_version)]
if empty.is_empty() &&
(ord_marker.as_bytes() == b"ord" || ord_marker.as_bytes() == PROTOCOL_ID) => {
contains_ord_marker = true;
if i + 4 < instructions.len() {
if let Instruction::PushBytes(ct) = &instructions[i+4] {
content_type = Some(ct.as_bytes().to_vec());
if i + 6 < instructions.len() && is_empty_push(&instructions[i+5]) {
if let Instruction::PushBytes(data) = &instructions[i+6] {
content_data = Some(data.as_bytes().to_vec());
}
}
}
}
break;
}
_ => {}
}
}
if contains_ord_marker {
let inscription = Inscription {
content_type,
body: content_data,
..Default::default()
};
all_inscriptions.push(inscription);
continue;
}
}
let mut envelopes = Vec::new();
let mut in_envelope = false;
let mut current_instructions = Vec::new();
for instruction in instructions.iter() {
match (&instruction, in_envelope) {
(Instruction::Op(OP_PUSHBYTES_0), false) => {
in_envelope = true;
current_instructions.push(instruction.clone());
}
(Instruction::Op(OP_IF), true) => {
current_instructions.push(instruction.clone());
}
(Instruction::Op(OP_ENDIF), true) => {
in_envelope = false;
current_instructions.push(instruction.clone());
if !current_instructions.is_empty() {
if let Ok(inscription) = parse_envelope(input_index, ¤t_instructions) {
envelopes.push(inscription);
}
current_instructions = Vec::new();
}
}
(_, true) => {
current_instructions.push(instruction.clone());
}
_ => {
}
}
}
if in_envelope && !current_instructions.is_empty() {
if let Ok(inscription) = parse_envelope(input_index, ¤t_instructions) {
envelopes.push(inscription);
}
}
all_inscriptions.extend(envelopes);
}
}
all_inscriptions
}
pub fn parse_envelope(_input_index: usize, instructions: &[Instruction]) -> Result<Inscription> {
let mut instructions = instructions.iter();
match instructions.next() {
Some(Instruction::Op(OP_PUSHBYTES_0)) => {}
_ => return Err(Error::ScriptError("Expected OP_0".to_string())),
}
match instructions.next() {
Some(Instruction::Op(OP_IF)) => {}
_ => return Err(Error::ScriptError("Expected OP_IF".to_string())),
}
match instructions.next() {
Some(Instruction::PushBytes(protocol_id)) if protocol_id.as_bytes() == PROTOCOL_ID => {}
_ => return Err(Error::ScriptError("Invalid protocol ID".to_string())),
}
let mut fields = BTreeMap::new();
let mut field = None;
let mut body = Vec::new();
let mut unrecognized_even_field = false;
let duplicate_field = false;
let mut incomplete_field = false;
for instruction in instructions {
match instruction {
Instruction::PushBytes(bytes) => {
match field.take() {
Some(tag) if tag != BODY_TAG => {
fields
.entry(tag)
.or_insert_with(Vec::new)
.push(bytes.as_bytes());
}
Some(_) => {
body.push(bytes.as_bytes());
}
None => {
if bytes.as_bytes() == BODY_TAG {
field = Some(bytes.as_bytes());
} else if bytes.len() == 1 && bytes.as_bytes()[0] % 2 == 1 {
field = Some(bytes.as_bytes());
} else {
unrecognized_even_field = true;
}
}
}
}
Instruction::Op(OP_ENDIF) => {
if field.is_some() {
incomplete_field = true;
}
field = None;
break;
}
_ => {
return Err(Error::ScriptError("Invalid instruction".to_string()));
}
}
}
if field.is_some() {
incomplete_field = true;
}
let mut inscription = Inscription {
unrecognized_even_field,
duplicate_field,
incomplete_field,
..Default::default()
};
if let Some(value) = parse_tag_with_value(&mut fields, &[1]) {
inscription.content_type = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[9]) {
inscription.content_encoding = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[11]) {
inscription.delegate = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[5]) {
inscription.metadata = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[7]) {
inscription.metaprotocol = Some(value);
}
inscription.parents = parse_array_tag_with_values(&mut fields, &[3]);
if let Some(value) = parse_tag_with_value(&mut fields, &[2]) {
inscription.pointer = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[17]) {
inscription.properties = Some(value);
}
if let Some(value) = parse_tag_with_value(&mut fields, &[13]) {
inscription.rune = Some(value);
}
if !body.is_empty() {
inscription.body = Some(body.concat());
}
Ok(inscription)
}
fn parse_tag_with_value(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, tag: &[u8]) -> Option<Vec<u8>> {
let values = fields.get_mut(tag)?;
if values.is_empty() {
return None;
}
let value = values.remove(0).to_vec();
if values.is_empty() {
fields.remove(tag);
}
Some(value)
}
fn parse_array_tag_with_values(fields: &mut BTreeMap<&[u8], Vec<&[u8]>>, tag: &[u8]) -> Vec<Vec<u8>> {
fields
.remove(tag)
.unwrap_or_default()
.into_iter()
.map(|v| v.to_vec())
.collect()
}