From 4c64d8ded81f7d47ce1688b2bc303ac222d0a699 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 3 Apr 2024 19:59:04 -0400 Subject: [PATCH] Test validity of all opcodes --- crates/ordinals/src/runestone.rs | 80 ++++++++++++++++++++++++++++++++ docs/src/runes/specification.md | 9 ++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/crates/ordinals/src/runestone.rs b/crates/ordinals/src/runestone.rs index feb85dd08d..3697a27ca8 100644 --- a/crates/ordinals/src/runestone.rs +++ b/crates/ordinals/src/runestone.rs @@ -2104,4 +2104,84 @@ mod tests { }), ); } + + #[test] + fn all_pushdata_opcodes_are_valid() { + for i in 0..79 { + let mut script_pubkey = Vec::new(); + + script_pubkey.push(opcodes::all::OP_RETURN.to_u8()); + script_pubkey.push(Runestone::MAGIC_NUMBER.to_u8()); + script_pubkey.push(i); + + match i { + 0..=75 => { + for j in 0..i { + script_pubkey.push(if j % 2 == 0 { 1 } else { 0 }); + } + + if i % 2 == 1 { + script_pubkey.push(1); + script_pubkey.push(1); + } + } + 76 => { + script_pubkey.push(0); + } + 77 => { + script_pubkey.push(0); + script_pubkey.push(0); + } + 78 => { + script_pubkey.push(0); + script_pubkey.push(0); + script_pubkey.push(0); + script_pubkey.push(0); + } + _ => unreachable!(), + } + + assert_eq!( + Runestone::decipher(&Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: default(), + output: vec![TxOut { + script_pubkey: script_pubkey.into(), + value: 0, + },], + }) + .unwrap(), + Artifact::Runestone(Runestone::default()), + ); + } + } + + #[test] + fn all_non_pushdata_opcodes_are_invalid() { + for i in 79..=u8::MAX { + let mut script_pubkey = Vec::new(); + + script_pubkey.push(opcodes::all::OP_RETURN.to_u8()); + script_pubkey.push(Runestone::MAGIC_NUMBER.to_u8()); + script_pubkey.push(i); + + assert_eq!( + Runestone::decipher(&Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: default(), + output: vec![TxOut { + script_pubkey: script_pubkey.into(), + value: 0, + },], + }) + .unwrap(), + Artifact::Cenotaph(Cenotaph { + flaws: Flaw::Opcode.into(), + ..default() + }), + ); + } + } } diff --git a/docs/src/runes/specification.md b/docs/src/runes/specification.md index b247790bf2..77db3704af 100644 --- a/docs/src/runes/specification.md +++ b/docs/src/runes/specification.md @@ -114,9 +114,12 @@ OP_13`. If deciphering fails, later matching outputs are not considered. #### Assembling the Payload Buffer -The payload buffer is assembled by concatenating data pushes. If a non-data -push opcode is encountered, the deciphered runestone is a cenotaph with no -etching, mint, or edicts. +The payload buffer is assembled by concatenating data pushes, after `OP_13`, in +the matching script pubkey. + +Data pushes are opcodes 0 through 78 inclusive. If a non-data push opcode is +encountered, i.e., any opcode equal to or greater than opcode 79, the +deciphered runestone is a cenotaph with no etching, mint, or edicts. #### Decoding the Integer Sequence