transfer200,000,000,000.x@ai#76,228,934brc-20Inscription #65935010#65,935,010pngInscription #50392736#50,392,736htmlInscription #50392722#50,392,722htmlInscription #50392721#50,392,721htmlInscription #46328317#46,328,317pngInscription #45996122#45,996,122png (3, 0, 0, witness.clone()), ], ..Default::default() }); let first = InscriptionId { txid, index: 0 }; // normal let second = InscriptionId { txid, index: 1 }; // cursed reinscription let fourth = InscriptionId { txid, index: 3 }; // cursed but bound let ninth = InscriptionId { txid, index: 8 }; // cursed reinscription context.mine_blocks(1); context.index.assert_inscription_location( first, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, Some(50 * COIN_VALUE), ); context.index.assert_inscription_location( second, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, Some(50 * COIN_VALUE), ); context.index.assert_inscription_location( fourth, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 50 * COIN_VALUE, }, Some(100 * COIN_VALUE), #44,987,311text first, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 0, }, Some(50 * COIN_VALUE), ); context.index.assert_inscription_location( second, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 50 * COIN_VALUE, }, Some(100 * COIN_VALUE), ); context.index.assert_inscription_location( third, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 100 * COIN_VALUE, }, Some(150 * COIN_VALUE), ); assert_eq!( context .index .get_inscription_entry(first) .unwrap() .unwrap() .inscription_number, 0 ); assert_eq!( context .index .get_inscription_entry(second) .unwrap() .unwrap() .inscription_number, -1 ); assert_eq!( context .index .get_inscription_entry(third) #44,987,310text ); context.index.assert_inscription_location( ninth, SatPoint { outpoint: OutPoint { txid, vout: 0 }, offset: 100 * COIN_VALUE, }, Some(150 * COIN_VALUE), ); assert_eq!( context .index .get_inscription_entry(first) .unwrap() .unwrap() .inscription_number, 0 ); assert_eq!( context .index .get_inscription_id_by_inscription_number(-3) .unwrap() .unwrap(), fourth ); assert_eq!( context .index .get_inscription_entry(fourth) .unwrap() .unwrap() .inscription_number, -3 ); assert_eq!( context .index .get_inscription_entry(ninth) .unwrap() .unwrap() .inscription_number, -8 ); } } #[test] fn genesis_fee_distributed_evenly() { for context in Context::configurations() { #44,987,296text context.mine_blocks(1); let witness = envelope(&[ b"ord", &[1], b"text/plain;charset=utf-8", &[2], b"bar", &[4], b"ord", ]); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0, witness)], ..Default::default() }); let inscription_id = InscriptionId { txid, index: 0 }; context.mine_blocks(1); context.index.assert_inscription_location( inscription_id, SatPoint { outpoint: unbound_outpoint(), offset: 0, }, None, ); assert_eq!( context .index .get_inscription_entry(inscription_id) .unwrap() .unwrap() .inscription_number, -1 ); } } #[test] fn duplicate_field_inscriptions_are_cursed() { for context in Context::configurations() { context.mine_blocks(1); let witness = envelope(&[ b"ord", &[1], #44,987,291text .unwrap() .unwrap() .inscription_number, -2 ); } } #[test] fn multiple_inscriptions_same_input_are_cursed_reinscriptions() { for context in Context::configurations() { context.rpc_server.mine_blocks(1); let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) .push_slice(b"ord") .push_slice([1]) .push_slice(b"text/plain;charset=utf-8") .push_slice([]) .push_slice(b"foo") .push_opcode(opcodes::all::OP_ENDIF) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) .push_slice(b"ord") .push_slice([1]) .push_slice(b"text/plain;charset=utf-8") .push_slice([]) .push_slice(b"bar") .push_opcode(opcodes::all::OP_ENDIF) .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) .push_slice(b"ord") .push_slice([1]) .push_slice(b"text/plain;charset=utf-8") #44,987,266text ..Default::default() }); let inscription_id = InscriptionId { txid, index: 0 }; context.mine_blocks(1); context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 1, 0, Default::default())], fee: 50 * COIN_VALUE, ..Default::default() }); let coinbase_tx = context.mine_blocks(1)[0].txdata[0].txid(); context.index.assert_inscription_location( inscription_id, SatPoint { outpoint: OutPoint { txid: coinbase_tx, vout: 0, }, offset: 50 * COIN_VALUE, }, Some(50 * COIN_VALUE), ); } } #[test] fn two_input_fee_spent_inscriptions_are_tracked_correctly() { for context in Context::configurations() { context.mine_blocks(2); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())], ..Default::default() }); let inscription_id = InscriptionId { txid, index: 0 }; #44,987,243textuse super::*; #[derive(Deserialize, Default, PartialEq, Debug)] #[serde(deny_unknown_fields)] pub(crate) struct Config { pub(crate) hidden: HashSet<InscriptionId>, pub(crate) bitcoin_rpc_pass: Option<String>, pub(crate) bitcoin_rpc_user: Option<String>, } impl Config { pub(crate) fn is_hidden(&self, inscription_id: InscriptionId) -> bool { self.hidden.contains(&inscription_id) } } #[cfg(test)] mod tests { use super::*; #[test] fn inscriptions_can_be_hidden() { let a = "8d363b28528b0cb86b5fd48615493fb175bdf132d2a3d20b4251bba3f130a5abi0" .parse::<InscriptionId>() .unwrap(); let b = "8d363b28528b0cb86b5fd48615493fb175bdf132d2a3d20b4251bba3f130a5abi1" .parse::<InscriptionId>() .unwrap(); let config = Config { hidden: iter::once(a).collect(), ..Default::default() }; assert!(config.is_hidden(a)); assert!(!config.is_hidden(b)); } #[test] fn example_config_file_is_valid() { let _: Config = serde_yaml::from_reader(File::open("ord.yaml").unwrap()).unwrap(); #44,985,746textpub(crate) use { super::*, bitcoin::{ blockdata::{opcodes, script, script::PushBytesBuf}, ScriptBuf, Witness, }, pretty_assertions::assert_eq as pretty_assert_eq, std::iter, test_bitcoincore_rpc::TransactionTemplate, unindent::Unindent, }; macro_rules! assert_regex_match { ($value:expr, $pattern:expr $(,)?) => { let regex = Regex::new(&format!("^(?s){}$", $pattern)).unwrap(); let string = $value.to_string(); if !regex.is_match(string.as_ref()) { panic!( "Regex:\n\n{}\n\n…did not match string:\n\n{}", regex, string ); } }; } macro_rules! assert_matches { ($expression:expr, $( $pattern:pat_param )|+ $( if $guard:expr )? $(,)?) => { match $expression { $( $pattern )|+ $( if $guard )? => {} left => panic!( "assertion failed: (left ~= right)\n left: `{:?}`\n right: `{}`", left, stringify!($($pattern)|+ $(if $guard)?) ), } } } pub(crate) fn blockhash(n: u64) -> BlockHash { let hex = format!("{n:x}");#44,985,565text if hex.is_empty() || hex.len() > 1 { panic!(); } hex.repeat(64).parse().unwrap() } pub(crate) fn txid(n: u64) -> Txid { let hex = format!("{n:x}"); if hex.is_empty() || hex.len() > 1 { panic!(); } hex.repeat(64).parse().unwrap() } pub(crate) fn outpoint(n: u64) -> OutPoint { format!("{}:{}", txid(n), n).parse().unwrap() } pub(crate) fn satpoint(n: u64, offset: u64) -> SatPoint { SatPoint { outpoint: outpoint(n), offset, } } pub(crate) fn address() -> Address { "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" .parse::<Address<NetworkUnchecked>>() .unwrap() .assume_checked() } pub(crate) fn recipient() -> Address { "tb1q6en7qjxgw4ev8xwx94pzdry6a6ky7wlfeqzunz" .parse::<Address<NetworkUnchecked>>() .unwrap() .assume_checked() } pub(crate) fn change(n: u64) -> Address { match n { 0 => "tb1qjsv26lap3ffssj6hfy8mzn0lg5vte6a42j75ww", 1 => "tb1qakxxzv9n7706kc3xdcycrtfv8cqv62hnwexc0l", 2 => "tb1qxz9yk0td0yye009gt6ayn7jthz5p07a75luryg", 3 => "tb1qe62s57n77pfhlw2vtqlhm87dwj75l6fguavjjq", #44,985,551texttransfer50,000.mice#43,873,689brc-20