From cf03e42179246ec074a6972e9658f844490ec495 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 3 Jan 2024 12:32:09 -0800 Subject: [PATCH 1/9] Add vindicated charm --- src/index.rs | 24 +++++++++++----------- src/index/updater/inscription_updater.rs | 18 +++++++++++----- src/inscriptions/charm.rs | 26 ++++++++++++++---------- src/subcommand/server.rs | 5 +++++ src/templates/inscription.rs | 1 + tests/json_api.rs | 19 +++++++++-------- 6 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/index.rs b/src/index.rs index 20c7bd0740..cd587aa066 100644 --- a/src/index.rs +++ b/src/index.rs @@ -87,18 +87,18 @@ pub enum List { #[derive(Copy, Clone)] pub(crate) enum Statistic { Schema = 0, - BlessedInscriptions, - Commits, - CursedInscriptions, - IndexRunes, - IndexSats, - LostSats, - OutputsTraversed, - ReservedRunes, - Runes, - SatRanges, - UnboundInscriptions, - IndexTransactions, + BlessedInscriptions = 1, + Commits = 2, + CursedInscriptions = 3, + IndexRunes = 4, + IndexSats = 5, + LostSats = 6, + OutputsTraversed = 7, + ReservedRunes = 8, + Runes = 9, + SatRanges = 10, + UnboundInscriptions = 11, + IndexTransactions = 12, } impl Statistic { diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index a491950073..3fb1ad0e15 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -30,6 +30,7 @@ enum Origin { pointer: Option, reinscription: bool, unbound: bool, + vindicated: bool, }, Old { old_satpoint: SatPoint, @@ -76,6 +77,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { let mut floating_inscriptions = Vec::new(); let mut id_counter = 0; let mut inscribed_offsets = BTreeMap::new(); + let jubilant = self.height >= self.chain.jubilee_height(); let mut total_input_value = 0; let total_output_value = tx.output.iter().map(|txout| txout.value).sum::(); @@ -142,9 +144,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { index: id_counter, }; - let curse = if self.height >= self.chain.jubilee_height() { - None - } else if inscription.payload.unrecognized_even_field { + let curse = if inscription.payload.unrecognized_even_field { Some(Curse::UnrecognizedEvenField) } else if inscription.payload.duplicate_field { Some(Curse::DuplicateField) @@ -187,6 +187,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { None }; + let vindicated = jubilant && curse.is_some(); + let unbound = current_input_value == 0 || curse == Some(Curse::UnrecognizedEvenField) || inscription.payload.unrecognized_even_field; @@ -201,13 +203,14 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { inscription_id, offset, origin: Origin::New { - reinscription: inscribed_offsets.get(&offset).is_some(), - cursed: curse.is_some(), + cursed: curse.is_some() && !jubilant, fee: 0, hidden: inscription.payload.hidden(), parent: inscription.payload.parent(), pointer: inscription.payload.pointer(), + reinscription: inscribed_offsets.get(&offset).is_some(), unbound, + vindicated, }, }); @@ -404,6 +407,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { pointer: _, reinscription, unbound, + vindicated, } => { let inscription_number = if cursed { let number: i32 = self.cursed_inscription_count.try_into().unwrap(); @@ -467,6 +471,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Charm::Unbound.set(&mut charms); } + if vindicated { + Charm::Vindicated.set(&mut charms); + } + if let Some(Sat(n)) = sat { self.sat_to_sequence_number.insert(&n, &sequence_number)?; } diff --git a/src/inscriptions/charm.rs b/src/inscriptions/charm.rs index b80c5c6616..cc6d77a755 100644 --- a/src/inscriptions/charm.rs +++ b/src/inscriptions/charm.rs @@ -1,19 +1,20 @@ #[derive(Copy, Clone)] pub(crate) enum Charm { - Coin, - Cursed, - Epic, - Legendary, - Lost, - Nineball, - Rare, - Reinscription, - Unbound, - Uncommon, + Coin = 0, + Cursed = 1, + Epic = 2, + Legendary = 3, + Lost = 4, + Nineball = 5, + Rare = 6, + Reinscription = 7, + Unbound = 8, + Uncommon = 9, + Vindicated = 10, } impl Charm { - pub(crate) const ALL: [Charm; 10] = [ + pub(crate) const ALL: [Charm; 11] = [ Self::Coin, Self::Uncommon, Self::Rare, @@ -24,6 +25,7 @@ impl Charm { Self::Cursed, Self::Unbound, Self::Lost, + Self::Vindicated, ]; fn flag(self) -> u16 { @@ -50,6 +52,7 @@ impl Charm { Self::Reinscription => "♻️", Self::Unbound => "🔓", Self::Uncommon => "🌱", + Self::Vindicated => "❤️‍🔥", } } @@ -65,6 +68,7 @@ impl Charm { Self::Reinscription => "reinscription", Self::Unbound => "unbound", Self::Uncommon => "uncommon", + Self::Vindicated => "vindicated", } } } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 3f54bde538..034172e206 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1219,6 +1219,11 @@ impl Server { Ok(if accept_json { Json(InscriptionJson { inscription_id: info.entry.id, + charms: Charm::ALL + .iter() + .filter(|charm| charm.is_set(info.charms)) + .map(|charm| charm.title().into()) + .collect(), children: info.children, inscription_number: info.entry.inscription_number, genesis_height: info.entry.height, diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 4faff866ab..2470424a35 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -23,6 +23,7 @@ pub(crate) struct InscriptionHtml { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct InscriptionJson { pub address: Option, + pub charms: Vec, pub children: Vec, pub content_length: Option, pub content_type: Option, diff --git a/tests/json_api.rs b/tests/json_api.rs index ab3dfb8e8e..696c4d0ae7 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -147,22 +147,23 @@ fn get_inscription() { pretty_assert_eq!( inscription_json, InscriptionJson { - parent: None, + address: None, + charms: vec!["coin".into(), "uncommon".into()], children: Vec::new(), + content_length: Some(3), + content_type: Some("text/plain;charset=utf-8".to_string()), + genesis_fee: 138, + genesis_height: 2, inscription_id, inscription_number: 0, - genesis_height: 2, - genesis_fee: 138, + next: None, output_value: Some(10000), - address: None, + parent: None, + previous: None, + rune: None, sat: Some(ord::Sat(50 * COIN_VALUE)), satpoint: SatPoint::from_str(&format!("{}:{}:{}", reveal, 0, 0)).unwrap(), - content_type: Some("text/plain;charset=utf-8".to_string()), - content_length: Some(3), timestamp: 2, - previous: None, - next: None, - rune: None, } ) } From 4f5546d50142ff46f709c6feea038ab449cd6cd9 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:17:42 -0800 Subject: [PATCH 2/9] Add tests --- src/index.rs | 171 ++++++++++++++++++++++++++++++++++++++ src/inscriptions/charm.rs | 9 ++ 2 files changed, 180 insertions(+) diff --git a/src/index.rs b/src/index.rs index cd587aa066..e611e5b463 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5590,4 +5590,175 @@ mod tests { ); } } + + // todo: + // - write tests + // + // - test reinscription edge case: + // - pre-jubilee, inscribe using pushnum: cursed + // - inscribe again on same sat: blessed + // + // - pre-jubilee, inscribe using pushnum: cursed + // - post-jubilee, inscribe using pushnum: vindicated + // + // Re-inscribing on a vindicated inscription will continue + // to yield a vindicated inscription, which may inconsistent + // with the process before Jubilee. + // + // Re-inscribing on a vindicated inscription will continue + // to yield a vindicated inscription, which may inconsistent + // with the process before Jubilee. + // + // ``` + // .inscription_number + // < 0; + // if initial_inscription_is_cursed { + // ``` + // + // We can fix that by also checking the vindicated charm. + // initial_inscription_is_cursed should be true if either + // inscription_number < 0 or inscription has vindicated charm. + // + // yes, we also think this is what we want, and vindicated + // will be consistant with release 0.9 after being fixed + // + //``` + // let entry = InscriptionEntry::load( + // self + // .sequence_number_to_entry + // .get(initial_inscription_sequence_number)? + // .unwrap() + // .value(), + //); + //``` + + #[test] + fn pre_jubilee_first_reinscription_after_cursed_inscription_is_blessed() { + for context in Context::configurations() { + context.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([]) + .push_opcode(opcodes::all::OP_PUSHNUM_1) + .push_opcode(opcodes::all::OP_ENDIF) + .into_script(); + + let witness = Witness::from_slice(&[script.into_bytes(), Vec::new()]); + + 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); + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + + dbg!(Charm::charms(entry.charms)); + + let sat = entry.sat; + + assert_eq!(entry.inscription_number, -1); + + let inscription = Inscription::default(); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0, inscription.to_witness())], + fee: 50 * COIN_VALUE, + ..Default::default() + }); + + let blocks = context.mine_blocks(1); + + let inscription_id = InscriptionId { txid, index: 0 }; + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + + dbg!(Charm::charms(entry.charms)); + + assert_eq!(entry.inscription_number, 0); + + assert_eq!(sat, entry.sat); + } + } + + #[test] + fn post_jubilee_first_reinscription_after_cursed_inscription_not_vindicated() { + for context in Context::configurations() { + context.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([]) + .push_opcode(opcodes::all::OP_PUSHNUM_1) + .push_opcode(opcodes::all::OP_ENDIF) + .into_script(); + + let witness = Witness::from_slice(&[script.into_bytes(), Vec::new()]); + + 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); + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + + dbg!(Charm::charms(entry.charms)); + + let sat = entry.sat; + + assert_eq!(entry.inscription_number, -1); + + context.mine_blocks(120); + + let inscription = Inscription::default(); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0, inscription.to_witness())], + fee: 50 * COIN_VALUE, + ..Default::default() + }); + + let blocks = context.mine_blocks(1); + + let inscription_id = InscriptionId { txid, index: 0 }; + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + + dbg!(Charm::charms(entry.charms)); + + assert!(!Charm::Vindicated.is_set(dbg!(entry.charms))); + + assert_eq!(entry.inscription_number, 0); + + assert_eq!(sat, entry.sat); + } + } } diff --git a/src/inscriptions/charm.rs b/src/inscriptions/charm.rs index cc6d77a755..33ede8fca6 100644 --- a/src/inscriptions/charm.rs +++ b/src/inscriptions/charm.rs @@ -71,4 +71,13 @@ impl Charm { Self::Vindicated => "vindicated", } } + + #[cfg(test)] + pub(crate) fn charms(charms: u16) -> Vec<&'static str> { + Self::ALL + .iter() + .filter(|charm| charm.is_set(charms)) + .map(|charm| charm.title()) + .collect() + } } From ab18e5f548eb178aaaae73c2ee9c12822de12abe Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:25:05 -0800 Subject: [PATCH 3/9] Tweak --- src/index.rs | 36 ++++++++++++++++++++++++++++-------- src/inscriptions/charm.rs | 6 +++--- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/index.rs b/src/index.rs index e611e5b463..9efc613257 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5663,7 +5663,10 @@ mod tests { .unwrap() .unwrap(); - dbg!(Charm::charms(entry.charms)); + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Cursed) + .is_some()); let sat = entry.sat; @@ -5677,7 +5680,7 @@ mod tests { ..Default::default() }); - let blocks = context.mine_blocks(1); + context.mine_blocks(1); let inscription_id = InscriptionId { txid, index: 0 }; @@ -5687,10 +5690,13 @@ mod tests { .unwrap() .unwrap(); - dbg!(Charm::charms(entry.charms)); - assert_eq!(entry.inscription_number, 0); + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Cursed) + .is_none()); + assert_eq!(sat, entry.sat); } } @@ -5726,7 +5732,15 @@ mod tests { .unwrap() .unwrap(); - dbg!(Charm::charms(entry.charms)); + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Cursed) + .is_some()); + + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Vindicated) + .is_none()); let sat = entry.sat; @@ -5742,7 +5756,7 @@ mod tests { ..Default::default() }); - let blocks = context.mine_blocks(1); + context.mine_blocks(1); let inscription_id = InscriptionId { txid, index: 0 }; @@ -5752,9 +5766,15 @@ mod tests { .unwrap() .unwrap(); - dbg!(Charm::charms(entry.charms)); + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Cursed) + .is_none()); - assert!(!Charm::Vindicated.is_set(dbg!(entry.charms))); + assert!(Charm::charms(entry.charms) + .iter() + .find(|charm| **charm == Charm::Vindicated) + .is_none()); assert_eq!(entry.inscription_number, 0); diff --git a/src/inscriptions/charm.rs b/src/inscriptions/charm.rs index 33ede8fca6..d0770886d7 100644 --- a/src/inscriptions/charm.rs +++ b/src/inscriptions/charm.rs @@ -1,4 +1,4 @@ -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum Charm { Coin = 0, Cursed = 1, @@ -73,11 +73,11 @@ impl Charm { } #[cfg(test)] - pub(crate) fn charms(charms: u16) -> Vec<&'static str> { + pub(crate) fn charms(charms: u16) -> Vec { Self::ALL .iter() .filter(|charm| charm.is_set(charms)) - .map(|charm| charm.title()) + .cloned() .collect() } } From f4f6836ecb52b8fc8c2ed6fa2cdb79dfe8f9f9ce Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:38:53 -0800 Subject: [PATCH 4/9] Tweak --- src/index.rs | 141 +++++++++++++---------- src/index/updater/inscription_updater.rs | 9 +- 2 files changed, 83 insertions(+), 67 deletions(-) diff --git a/src/index.rs b/src/index.rs index 9efc613257..11c39b6c12 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5591,47 +5591,6 @@ mod tests { } } - // todo: - // - write tests - // - // - test reinscription edge case: - // - pre-jubilee, inscribe using pushnum: cursed - // - inscribe again on same sat: blessed - // - // - pre-jubilee, inscribe using pushnum: cursed - // - post-jubilee, inscribe using pushnum: vindicated - // - // Re-inscribing on a vindicated inscription will continue - // to yield a vindicated inscription, which may inconsistent - // with the process before Jubilee. - // - // Re-inscribing on a vindicated inscription will continue - // to yield a vindicated inscription, which may inconsistent - // with the process before Jubilee. - // - // ``` - // .inscription_number - // < 0; - // if initial_inscription_is_cursed { - // ``` - // - // We can fix that by also checking the vindicated charm. - // initial_inscription_is_cursed should be true if either - // inscription_number < 0 or inscription has vindicated charm. - // - // yes, we also think this is what we want, and vindicated - // will be consistant with release 0.9 after being fixed - // - //``` - // let entry = InscriptionEntry::load( - // self - // .sequence_number_to_entry - // .get(initial_inscription_sequence_number)? - // .unwrap() - // .value(), - //); - //``` - #[test] fn pre_jubilee_first_reinscription_after_cursed_inscription_is_blessed() { for context in Context::configurations() { @@ -5665,8 +5624,11 @@ mod tests { assert!(Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Cursed) - .is_some()); + .any(|charm| *charm == Charm::Cursed)); + + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Vindicated)); let sat = entry.sat; @@ -5676,7 +5638,6 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 1, 0, inscription.to_witness())], - fee: 50 * COIN_VALUE, ..Default::default() }); @@ -5692,19 +5653,51 @@ mod tests { assert_eq!(entry.inscription_number, 0); + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Cursed)); + + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Vindicated)); + + assert_eq!(sat, entry.sat); + + let inscription = Inscription::default(); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(3, 1, 0, inscription.to_witness())], + ..Default::default() + }); + + context.mine_blocks(1); + + let inscription_id = InscriptionId { txid, index: 0 }; + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + assert!(Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Cursed) - .is_none()); + .any(|charm| *charm == Charm::Cursed)); + + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Vindicated)); + + assert_eq!(entry.inscription_number, -2); assert_eq!(sat, entry.sat); } } #[test] - fn post_jubilee_first_reinscription_after_cursed_inscription_not_vindicated() { + fn post_jubilee_first_reinscription_after_vindicated_inscription_not_vindicated() { for context in Context::configurations() { - context.mine_blocks(1); + context.mine_blocks(110); let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) @@ -5732,27 +5725,51 @@ mod tests { .unwrap() .unwrap(); - assert!(Charm::charms(entry.charms) + assert!(!Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Cursed) - .is_some()); + .any(|charm| *charm == Charm::Cursed)); assert!(Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Vindicated) - .is_none()); + .any(|charm| *charm == Charm::Vindicated)); let sat = entry.sat; - assert_eq!(entry.inscription_number, -1); + assert_eq!(entry.inscription_number, 0); + + let inscription = Inscription::default(); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(111, 1, 0, inscription.to_witness())], + ..Default::default() + }); + + context.mine_blocks(1); + + let inscription_id = InscriptionId { txid, index: 0 }; + + let entry = context + .index + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap(); + + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Cursed)); + + assert!(!Charm::charms(entry.charms) + .iter() + .any(|charm| *charm == Charm::Vindicated)); - context.mine_blocks(120); + assert_eq!(entry.inscription_number, 1); + + assert_eq!(sat, entry.sat); let inscription = Inscription::default(); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { - inputs: &[(2, 1, 0, inscription.to_witness())], - fee: 50 * COIN_VALUE, + inputs: &[(112, 1, 0, inscription.to_witness())], ..Default::default() }); @@ -5766,17 +5783,15 @@ mod tests { .unwrap() .unwrap(); - assert!(Charm::charms(entry.charms) + assert!(!Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Cursed) - .is_none()); + .any(|charm| *charm == Charm::Cursed)); assert!(Charm::charms(entry.charms) .iter() - .find(|charm| **charm == Charm::Vindicated) - .is_none()); + .any(|charm| *charm == Charm::Vindicated)); - assert_eq!(entry.inscription_number, 0); + assert_eq!(entry.inscription_number, 2); assert_eq!(sat, entry.sat); } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 3fb1ad0e15..417f790f8d 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -167,15 +167,16 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { let initial_inscription_sequence_number = self.id_to_sequence_number.get(id.store())?.unwrap().value(); - let initial_inscription_is_cursed = InscriptionEntry::load( + let entry = InscriptionEntry::load( self .sequence_number_to_entry .get(initial_inscription_sequence_number)? .unwrap() .value(), - ) - .inscription_number - < 0; + ); + + let initial_inscription_is_cursed = + entry.inscription_number < 0 || Charm::Vindicated.is_set(entry.charms); if initial_inscription_is_cursed { None From 56293a8e92950c952b41f24a037c51917f87c122 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:48:10 -0800 Subject: [PATCH 5/9] Tweak --- src/index.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/index.rs b/src/index.rs index 11c39b6c12..55d18aaed7 100644 --- a/src/index.rs +++ b/src/index.rs @@ -5596,6 +5596,9 @@ mod tests { for context in Context::configurations() { context.mine_blocks(1); + // Before the jubilee, an inscription on a sat using a pushnum opcode is + // cursed and not vindicated. + let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) .push_opcode(opcodes::all::OP_IF) @@ -5634,6 +5637,9 @@ mod tests { assert_eq!(entry.inscription_number, -1); + // Before the jubilee, reinscription on the same sat is not cursed and + // not vindicated. + let inscription = Inscription::default(); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { @@ -5663,6 +5669,9 @@ mod tests { assert_eq!(sat, entry.sat); + // Before the jubilee, a third reinscription on the same sat is cursed + // and not vindicated. + let inscription = Inscription::default(); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { @@ -5698,6 +5707,8 @@ mod tests { fn post_jubilee_first_reinscription_after_vindicated_inscription_not_vindicated() { for context in Context::configurations() { context.mine_blocks(110); + // After the jubilee, an inscription on a sat using a pushnum opcode is + // vindicated and not cursed. let script = script::Builder::new() .push_opcode(opcodes::OP_FALSE) @@ -5737,6 +5748,9 @@ mod tests { assert_eq!(entry.inscription_number, 0); + // After the jubilee, a reinscription on the same is not cursed and not + // vindicated. + let inscription = Inscription::default(); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { @@ -5766,6 +5780,9 @@ mod tests { assert_eq!(sat, entry.sat); + // After the jubilee, a third reinscription on the same is vindicated and + // not cursed. + let inscription = Inscription::default(); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { From da3821bfab65d57d8f0e8ada9016ac103d58ba68 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:53:17 -0800 Subject: [PATCH 6/9] Tweak --- src/subcommand/server.rs | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 034172e206..fc1edd9058 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -4326,7 +4326,7 @@ next } #[test] - fn charm_cursed() { + fn charm_cursed_present_before_jubilee() { let server = TestServer::new_with_regtest(); server.mine_blocks(2); @@ -4364,6 +4364,45 @@ next ); } + #[test] + fn charm_vindicated() { + let server = TestServer::new_with_regtest(); + + server.mine_blocks(110); + + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[ + (1, 0, 0, Witness::default()), + (2, 0, 0, inscription("text/plain", "cursed").to_witness()), + ], + outputs: 2, + ..Default::default() + }); + + let id = InscriptionId { txid, index: 0 }; + + server.mine_blocks(1); + + server.assert_response_regex( + format!("/inscription/{id}"), + StatusCode::OK, + format!( + ".*

Inscription 0

.* +
+
id
+
{id}
+
charms
+
+ ❤️‍🔥 +
+ .* +
+.* +" + ), + ); + } + #[test] fn charm_coin() { let server = TestServer::new_with_regtest_with_index_sats(); From 067333fdd3794f7ddd80a78ba03dbfae9575d04c Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:57:41 -0800 Subject: [PATCH 7/9] Tweak --- src/index/updater/inscription_updater.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 417f790f8d..72f7ecef09 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -175,10 +175,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .value(), ); - let initial_inscription_is_cursed = + let initial_inscription_was_cursed_or_vindicated = entry.inscription_number < 0 || Charm::Vindicated.is_set(entry.charms); - if initial_inscription_is_cursed { + if initial_inscription_was_cursed_or_vindicated { None } else { Some(Curse::Reinscription) From dd04a616128f9f4bafa3423b124be945469c518a Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 15:58:32 -0800 Subject: [PATCH 8/9] Tweak --- src/index/updater/inscription_updater.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 72f7ecef09..19f99033cc 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -188,8 +188,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { None }; - let vindicated = jubilant && curse.is_some(); - let unbound = current_input_value == 0 || curse == Some(Curse::UnrecognizedEvenField) || inscription.payload.unrecognized_even_field; @@ -211,7 +209,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { pointer: inscription.payload.pointer(), reinscription: inscribed_offsets.get(&offset).is_some(), unbound, - vindicated, + vindicated: curse.is_some() && jubilant, }, }); From 6d4d2e7864774738f6c36769ef093bc077c4fc47 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 4 Jan 2024 16:10:58 -0800 Subject: [PATCH 9/9] Tweak --- src/subcommand/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 1c794905c9..925451d1c7 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -4346,7 +4346,7 @@ next } #[test] - fn charm_cursed_present_before_jubilee() { + fn charm_cursed() { let server = TestServer::new_with_regtest(); server.mine_blocks(2);